I am writing a C# idle monitor using mouse and keyboard windows hook
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);
}
}
In the above code, HookCallback is passed into SetWindowsHookEx. The code compiles and runs normally but at some time later, HookCallback gets garbage collected and visual studio debugger will catch it.
In the code below, HookCallback is assigned indirectly using _HookProc, and so far it seems to be running OK. I can see why the below code works, but I am not sure why when assigned directly (as above), HookCallback is not referenced ("stored") somewhere that will not cause it to be GC-ed?
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)
{
// ...
}
}
Remember that, in general, the system has no knowledge of which native functions you're going to P/Invoke into. For most circumstances, so long as the function pointer that you're passing into a native function is only (possibly) used whilst that function call is in progress, the .NET system already does enough to ensure that the function can be called.
But in your circumstances, you're passing a function pointer across that is going to be invoked at other points in time, well after SetWindowsHookEx has returned. In such a circumstance, it's your responsibility to ensure that the delegate stays alive for long enough.
And, by the look of things, you've already found a way to achieve that.
The alternatives would be a) Any delegate passed to any native function is never garbage collected, creating a possible memory leak, or b) The system would have to provide a mechanism for your code to indicate when it knows that the delegate is no longer required. But such a mechanism would still require the co-operation of your code, more-so than just having you manage the lifetime of the managed object.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With