c# - 为什么委托(delegate)函数引用在作为参数传递时会被垃圾收集?

标签 c# garbage-collection

我正在使用鼠标和键盘窗口钩子(Hook)编写一个 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 没有被引用(“存储”)在某个不会导致它被 GC 编辑的地方?

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)
    {
        // ...
    }
}

最佳答案

请记住,一般来说,系统不知道您要 P/Invoke 到哪些 native 函数。在大多数情况下,只要您传递给 native 函数的函数指针仅(可能)在该函数调用正在进行时使用,.NET 系统就已经足以确保可以调用该函数。

但在您的情况下,您正在传递一个函数指针,它将在 SetWindowsHookEx 返回后的其他时间点被调用。在这种情况下,有责任确保代理存活足够长的时间。

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


备选方案是 a) 传递给任何 native 函数的任何委托(delegate)都从不被垃圾回收,从而造成可能的内存泄漏,或者 b) 系统必须为您的代码 以指示它何时知道不再需要委托(delegate)。但是这样的机制仍然需要代码的协作,而不仅仅是让您管理托管对象的生命周期。

关于c# - 为什么委托(delegate)函数引用在作为参数传递时会被垃圾收集?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23014380/

相关文章:

c# - 如何查找和调用特定类型的 .Net TypeConverter?

android - 如何阻止垃圾收集器在android中被调用

c# - Confluence Kafka Consumer 类中 key 的反序列化意味着什么?

c# - 如何覆盖基类方法并添加参数

c# - 为什么对waveOutGetPosition的调用挂起?

c# - Socket c# 通过互联网聊天?

java - 循环 GC 在映射中起作用吗?

java - 将数组元素设置为 String 对象时,元素是否引用该对象?

javascript - 垃圾收集和 JavaScript "delete": Is this overkill/obfuscation, 还是一个好的做法?

c# - 限制 C# 应用程序中托管堆的大小