检测到globalKeyboardHook中的CallbackOnCollectedDelegate

我正在使用全局键盘钩子类。 此类允许检查键盘键是否按下任何位置。 过了一段时间我有一个错误:

**CallbackOnCollectedDelegate was detected** A callback was made on a garbage collected delegate of type 'Browser!Utilities.globalKeyboardHook+keyboardHookProc::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. 

这是globalkeyboardHook类:

  public delegate int keyboardHookProc(int code, int wParam, ref keyboardHookStruct lParam); public struct keyboardHookStruct { public int vkCode; public int scanCode; public int flags; public int time; public int dwExtraInfo; } const int WH_KEYBOARD_LL = 13; const int WM_KEYDOWN = 0x100; const int WM_KEYUP = 0x101; const int WM_SYSKEYDOWN = 0x104; const int WM_SYSKEYUP = 0x105; public List HookedKeys = new List(); IntPtr hhook = IntPtr.Zero; public event KeyEventHandler KeyDown; public event KeyEventHandler KeyUp; public globalKeyboardHook() { hook(); } ~globalKeyboardHook() { unhook(); } public void hook() { IntPtr hInstance = LoadLibrary("User32"); hhook = SetWindowsHookEx(WH_KEYBOARD_LL, hookProc, hInstance, 0); } public void unhook() { UnhookWindowsHookEx(hhook); } public int hookProc(int code, int wParam, ref keyboardHookStruct lParam) { if (code >= 0) { Keys key = (Keys)lParam.vkCode; if (HookedKeys.Contains(key)) { KeyEventArgs kea = new KeyEventArgs(key); if ((wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) && (KeyDown != null)) { KeyDown(this, kea); } else if ((wParam == WM_KEYUP || wParam == WM_SYSKEYUP) && (KeyUp != null)) { KeyUp(this, kea); } if (kea.Handled) return 1; } } return CallNextHookEx(hhook, code, wParam, ref lParam); } [DllImport("user32.dll")] static extern IntPtr SetWindowsHookEx(int idHook, keyboardHookProc callback, IntPtr hInstance, uint threadId); [DllImport("user32.dll")] static extern bool UnhookWindowsHookEx(IntPtr hInstance); [DllImport("user32.dll")] static extern int CallNextHookEx(IntPtr idHook, int nCode, int wParam, ref keyboardHookStruct lParam); [DllImport("kernel32.dll")] static extern IntPtr LoadLibrary(string lpFileName); #endregion 

任何想法如何解决它? 该程序运行良好,但一段时间后程序冻结ant我得到这个错误。

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

这是你的问题。 您依靠C#语法糖来自动创建一个hookProc的委托对象。 实际代码生成如下所示:

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

对委托对象只有一个引用,$ temp。 但它是局部变量,只要你的hook()方法停止执行并返回就会消失。 垃圾收集器无法看到Windows对它有一个“引用”,它无法探测非托管代码以获取引用。 因此,下次垃圾收集器运行时,委托对象将被销毁。 当Windows进行钩子回调时,这就是kaboom。 在程序与AccessViolation崩溃之前,内置MDA会检测到问题并生成有用的诊断信息。

您需要创建一个足够长的委托对象的附加引用。 例如,您可以使用GCHandle。 或者更简单,只需自己存储引用,以便垃圾收集器始终可以看到引用。 在您的class级中添加一个字段。 使其静态是确保无法收集对象的可靠方法:

  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; } 

无需pinvoke FreeLibrary,user32.dll始终加载,直到您的程序终止。