我正在使用鼠标和键盘窗口挂钩编写C#空闲监视器
public class KeyboardHook : WindowsHook
{
private static event KeyEventHandler _KeyDown = null;
public static void Hook(KeyEventHandler onKeyDown)
{
_KeyDown = onKeyDown;
SetWindowsHookEx(WH_KEYBOARD_LL, HookCallback); // PROBLEM!!
}
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && wParam == (IntPtr)KeyboardMessage.WM_KEYDOWN)
{
int vkCode = Marshal.ReadInt32(lParam);
_KeyDown(null, new KeyEventArgs((Keys)vkCode));
}
return CallNextHookEx(nCode, wParam, lParam);
}
}
在上面的代码中,HookCallback被传递到SetWindowsHookEx。代码编译并正常运行,但稍后,HookCallback会收集垃圾,Visual Studio调试器会捕获它。
在下面的代码中,HookCallback是使用_HookProc间接分配的,到目前为止它似乎运行正常。我可以看到为什么下面的代码有效,但我不确定为什么直接分配(如上所述),HookCallback没有被引用(&#34;存储&#34;)某处不会导致它被GC编辑?< / p>
public class KeyboardHook : WindowsHook
{
private static HOOKPROC _HookProc = HookCallback; // saving a reference locally
private static event KeyEventHandler _KeyDown = null;
public static void Hook(KeyEventHandler onKeyDown)
{
_KeyDown = onKeyDown;
SetWindowsHookEx(WH_KEYBOARD_LL, _HookProc);
}
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
// ...
}
}
答案 0 :(得分:4)
请记住,一般情况下,系统不知道您要进入P / Invoke的本机功能。对于大多数情况,只要在函数调用正在进行时(仅可能)使用您传入本机函数的函数指针,.NET系统已经足够确保可以调用该函数
但是在你的情况下,你将在SetWindowsHookEx
返回之后的其他时间点调用函数指针。在这种情况下,你的责任是确保代表保持足够长的时间。
而且,从表面上看,你已经找到了实现这一目标的方法。
替代方案是a)传递给任何本机函数的任何委托是从不垃圾收集,创建可能的内存泄漏,或b)系统必须提供你的机制代码表示何时知道不再需要委托。但是这样的机制仍然需要代码的合作,而不仅仅是管理托管对象的生命周期。