2

I m developing a Multi choices Exam System and needed to disable all the keyboard and just use mouse click, but I got this issue

A callback was made on a garbage collected delegate of type 'UI!UI.Forms.frmExamHome+LowLevelKeyboardProcDelegate::Invoke'. This may cause application crashes, corruption and data loss. When passing delegates to unmanaged code, they must be kept alive by the managed application until it is guaranteed that they will never be called.'

My code:

void wniDisable()
        {
           intLLKey = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, System.Runtime.InteropServices.Marshal.GetHINSTANCE(System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0]).ToInt32(), 0);

        }
        [DllImport("user32", EntryPoint = "SetWindowsHookExA", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
        public static extern int SetWindowsHookEx(int idHook,  LowLevelKeyboardProcDelegate lpfn, int hMod, int dwThreadId);
        [DllImport("user32", EntryPoint = "UnhookWindowsHookEx", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
        public static extern int UnhookWindowsHookEx(int hHook);
        public delegate int LowLevelKeyboardProcDelegate(int nCode, int wParam, ref KBDLLHOOKSTRUCT lParam);
        [DllImport("user32", EntryPoint = "CallNextHookEx", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
        public static extern int CallNextHookEx(int hHook, int nCode, int wParam, ref KBDLLHOOKSTRUCT lParam);
        public const int WH_KEYBOARD_LL = 13;

        /*code needed to disable start menu*/
        [DllImport("user32.dll")]
        private static extern int FindWindow(string className, string windowText);
        [DllImport("user32.dll")]
        private static extern int ShowWindow(int hwnd, int command);

        private const int SW_HIDE = 0;
        private const int SW_SHOW = 1;
        public struct KBDLLHOOKSTRUCT
        {
            public int vkCode;
            public int scanCode;
            public int flags;
            public int time;
            public int dwExtraInfo;
        }
        public static int intLLKey;

        public int LowLevelKeyboardProc(int nCode, int wParam, ref KBDLLHOOKSTRUCT lParam)
        {
            bool blnEat = false;

            switch (wParam)
            {
                case 256:
                case 257:
                case 260:
                case 261:
                    //Alt+Tab, Alt+Esc, Ctrl+Esc, Windows Key,
                    blnEat = ((lParam.vkCode == 9) && (lParam.flags == 32)) | ((lParam.vkCode == 27) && (lParam.flags == 32)) | ((lParam.vkCode == 27) && (lParam.flags == 0)) | ((lParam.vkCode == 91) && (lParam.flags == 1)) | ((lParam.vkCode == 92) && (lParam.flags == 1)) | ((lParam.vkCode == 73) && (lParam.flags == 0));
                    break;
            }

            if (blnEat == true)
            {
                return 1;
            }
            else
            {
                return CallNextHookEx(0, nCode, wParam, ref lParam);
            }
        }
        public void KillStartMenu()
        {
            int hwnd = FindWindow("Shell_TrayWnd", "");
            ShowWindow(hwnd, SW_HIDE);
        }
Vega
  • 27,856
  • 27
  • 95
  • 103
AmaniCodes
  • 21
  • 7
  • Does this answer your question? [CallbackOnCollectedDelegate in globalKeyboardHook was detected](https://stackoverflow.com/questions/9957544/callbackoncollecteddelegate-in-globalkeyboardhook-was-detected) – StayOnTarget Jan 14 '22 at 15:39

1 Answers1

0

You need to store the hook delegate as class member or the delegate instance will be method local and be garbage collected after you have left the method.

Check out https://github.com/Alois-xx/etwcontroller/blob/master/ETWController/Hooking/Hooker.cs

for a working sample.

   class Hooker : IDisposable
    {
        int MouseHookHandle;
        int KeyboardHookHandle;
        HookProc MouseHookGCRootedDelegate;
        HookProc KeyboardHookGCRootedDelegate;
..
    public Hooker()
        {
           // HookEvents.RegisterItself();
            MouseHookGCRootedDelegate = MouseHook;
            KeyboardHookGCRootedDelegate = KeyboardHook;
        }

        void HookKeyboard(bool bHook)
        {
            if (KeyboardHookHandle == 0 && bHook)
            {
                using (var mainMod = Process.GetCurrentProcess().MainModule)
                    KeyboardHookHandle = HookNativeDefinitions.SetWindowsHookEx(HookNativeDefinitions.WH_KEYBOARD_LL, KeyboardHookGCRootedDelegate, HookNativeDefinitions.GetModuleHandle(mainMod.ModuleName), 0);

                //If the SetWindowsHookEx function fails.
                if (KeyboardHookHandle == 0)
                {
                    System.Windows.MessageBox.Show("SetWindowsHookEx Failed " + new Win32Exception(Marshal.GetLastWin32Error()));
                    return;
                }
            }
            else if(bHook == false)
            {
                Debug.Print("Unhook keyboard");
                HookNativeDefinitions.UnhookWindowsHookEx(KeyboardHookHandle);
                KeyboardHookHandle = 0;
            }

        }
Alois Kraus
  • 13,229
  • 1
  • 38
  • 64
  • code is written in windows form should i edit form code or create class with your code above – AmaniCodes Jan 28 '19 at 10:27
  • @AmaniCodes: From a clean code perspective it would be cleaner to extract the hooking into a separate class. The SRP principle (Single Responsibility Principle) applies here. One class should have only one purpose. – Alois Kraus Jan 28 '19 at 12:49
  • i still need help to fix this error plz any help is appriciated – AmaniCodes Feb 02 '19 at 14:46
  • You can copy my class to your project. It should work and is pretty much self contained. – Alois Kraus Feb 02 '19 at 15:43
  • i copied same class and paste it in my project its give me multi errors so that its doesnt worked..... – AmaniCodes Feb 03 '19 at 06:45
  • Hello, I have now seen your .cs file on github. I'm just wondering what the "disposing" parameter is used for in the `Dispose` method. It is not used in the method at all – Rans May 01 '20 at 21:41
  • 1
    @Rans: There is no disposing parameter in the .cs File. Not sure what you are looking at. Anyway when you implement the IDisposable interface the code analyzers will beat you to that direction to implement it this way to allow you later to add a finalizer which will cleanup only unmanaged resources. That was at the beginning of .NET where no SafeHandles did exist and many classes could carry unmanaged resources with them. That was a bad pattern and abandoned in favor of small classes which wrap the unmanaged resource which derive from CriticalFinalizerObject. – Alois Kraus May 02 '20 at 04:55