为什么在作为参数传递时收集了委托函数引用垃圾?

时间:2014-04-11 13:43:48

标签: c# garbage-collection

我正在使用鼠标和键盘窗口挂钩编写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)
    {
        // ...
    }
}

1 个答案:

答案 0 :(得分:4)

请记住,一般情况下,系统不知道您要进入P / Invoke的本机功能。对于大多数情况,只要在函数调用正在进行时(仅可能)使用您传入本机函数的函数指针,.NET系统已经足够确保可以调用该函数

但是在你的情况下,你将在SetWindowsHookEx返回之后的其他时间点调用函数指针。在这种情况下,你的责任是确保代表保持足够长的时间。

而且,从表面上看,你已经找到了实现这一目标的方法。


替代方案是a)传递给任何本机函数的任何委托是从不垃圾收集,创建可能的内存泄漏,或b)系统必须提供你的机制代码表示何时知道不再需要委托。但是这样的机制仍然需要代码的合作,而不仅仅是管理托管对象的生命周期。