CallbackOnCollectedDelegate in globalKeyboardHook was detected

前端 未结 2 1451
无人及你
无人及你 2020-11-29 06:48

I\'m using a global keyboard hook class. This class allows to check if keyboard key pressed anywhere. And after some time I\'m having an error:

        **Cal         


        
2条回答
  •  孤独总比滥情好
    2020-11-29 07:10

    hhook = SetWindowsHookEx(WH_KEYBOARD_LL, hookProc, hInstance, 0);
    

    There's your problem. You are relying on C# syntax sugar to have it automatically create a delegate object to hookProc. Actual code generation look like this:

    keyboardHookProc $temp = new keyboardHookProc(hookProc);
    hhook = SetWindowsHookEx(WH_KEYBOARD_LL, $temp, hInstance, 0);
    

    There's only one reference to the delegate object, $temp. But it is local variable and disappears as soon as your hook() method stops executing and returns. The garbage collector is otherwise powerless to see that Windows has a 'reference' to it as well, it cannot probe unmanaged code for references. So the next time the garbage collector runs, the delegate object gets destroyed. And that's a kaboom when Windows makes the hook callback. The built-in MDA detects the problem and generates the helpful diagnostic before the program crashes with an AccessViolation.

    You will need to create an additional reference to the delegate object that survives long enough. You could use GCHandle for example. Or easier, just store a reference yourself so the garbage collector can always see the reference. Add a field to your class. Making it static is a sure-fire way to ensure the object can't be collected:

        private static keyboardHookProc callbackDelegate;
    
        public void hook()
        {
            if (callbackDelegate != null) throw new InvalidOperationException("Can't hook more than once");
            IntPtr hInstance = LoadLibrary("User32");
            callbackDelegate = new keyboardHookProc(hookProc);
            hhook = SetWindowsHookEx(WH_KEYBOARD_LL, callbackDelegate, hInstance, 0);
            if (hhook == IntPtr.Zero) throw new Win32Exception();
        }
    
        public void unhook()
        {
            if (callbackDelegate == null) return;
            bool ok = UnhookWindowsHookEx(hhook);
            if (!ok) throw new Win32Exception();
            callbackDelegate = null;
        }
    

    No need to pinvoke FreeLibrary, user32.dll is always loaded until your program terminates.

提交回复
热议问题