CallbackOnCollectedDelegate在globalKeyboardHook检测 [英] CallbackOnCollectedDelegate in globalKeyboardHook was detected

查看:147
本文介绍了CallbackOnCollectedDelegate在globalKeyboardHook检测的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用的是一个全球性的键盘钩子类。此类允许检查,如果键盘的键pressed任何地方。而且我有一个错误在一段时间后:

  **检测CallbackOnCollectedDelegate **回调是在浏览器!Utilities.globalKeyboardHook + keyboardHookProc ::调用类型的垃圾回收代表发表。这可能会导致应用程序崩溃,损坏和数据丢失。当传递委托给非托管code,它们必须由托管应用程序,直到它被保证,他们将永远不会被称为维持生命。

下面是globalkeyboardHook类:

 公共委托INT keyboardHookProc(INT code中,int wParam中,裁判keyboardHookStruct lParam的);        公共结构keyboardHookStruct
        {
            公众诠释VK code;
            公众诠释扫描code;
            公众诠释标志;
            公众诠释的时间;
            公众诠释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;        公开名单<&按键GT; HookedKeys =新的List<&按键GT;();        IntPtr的HHOOK = IntPtr.Zero;        公共事件KeyEventHandler的KeyDown;
        公共事件KeyEventHandler的KeyUp;        公共globalKeyboardHook()
        {
            钩();
        }        〜globalKeyboardHook()
        {
            脱钩();
        }        公共无效挂钩()
        {
            IntPtr的实例句柄调用LoadLibrary =(USER32);
            HHOOK = SetWindowsHookEx函数(WH_KEYBOARD_LL,HOOKPROC,实例句柄,0);
        }        公共无效脱钩()
        {
            UnhookWindowsHookEx(HHOOK);
        }        公众诠释HOOKPROC(INT code中,int wParam中,裁判keyboardHookStruct的lParam)
        {
            如果(code> = 0)
            {
                按键键=(键)lParam.vk code;
                如果(HookedKeys.Contains(键))
                {
                    KeyEventArgs KEA =新KeyEventArgs(密钥);
                    如果((的wParam == WM_KEYDOWN ||的wParam == WM_SYSKEYDOWN)及及(的KeyDown = NULL)!)
                    {
                        的KeyDown(这一点,KEA);
                    }
                    否则,如果((的wParam == || WM_KEYUP的wParam == WM_SYSKEYUP)及及(的KeyUp = NULL)!)
                    {
                        的KeyUp(这一点,KEA);
                    }
                    如果(kea.Handled)
                        返回1;
                }
            }
            返回CallNextHookEx方法(HHOOK,code,的wParam,lParam的参考);
        }        函数[DllImport(user32.dll中)]
        静态外部的IntPtr SetWindowsHookEx函数(INT idHook,keyboardHookProc回调,IntPtr的实例句柄,UINT的threadId);
        函数[DllImport(user32.dll中)]
        静态的extern BOOL UnhookWindowsHookEx(IntPtr的实例句柄);        函数[DllImport(user32.dll中)]
        静态外部INT CallNextHookEx方法(IntPtr的idHook,INT N code中,int wParam中,裁判keyboardHookStruct lParam的);        函数[DllImport(KERNEL32.DLL)]
        静态外部的IntPtr调用LoadLibrary(字符串lpFileName的对象);
        #endregion

任何想法如何解决?该项目运作良好,但过一段时间后,程序冻结蚂蚁我得到这个错误。


解决方案

  HHOOK =调用SetWindowsHookEx(WH_KEYBOARD_LL,HOOKPROC,实例句柄,0);

有是你的问题。你是依靠C#语法糖,系统将自动创建一个委托对象的 HOOKPROC 的。实际code一代是这样的:

  keyboardHookProc $ TEMP =新keyboardHookProc(HOOKPROC);
HHOOK = SetWindowsHookEx函数(WH_KEYBOARD_LL,$温度,实例句柄,0);

有只有一个参考的委托对象,$温度。但它是局部变量,一旦消失,你的钩子()方法停止执行并返回。垃圾收集器,否则无力地看到,Windows有一个参考,以它为好,不能探测非托管code的引用。因此,垃圾收集器下一次运行时,委托对象被销毁。这是一个KABOOM当Windows使钩子回调。内置MDA检测到问题,并与AccessViolation程序崩溃之前生成有用的诊断。

您需要创建一个额外的引用,该生存足够长的委托对象。你可以使用的GCHandle为例。或者更简单,只是存储参考自己这样的垃圾收集器总能看到参考。一个字段添加到您的类。使其静是一种安全可靠的方式,以确保对象不能收集:

 私有静态keyboardHookProc callbackDelegate;    公共无效挂钩()
    {
        如果(!callbackDelegate = NULL)抛出新的InvalidOperationException异常(无法勾超过一次);
        IntPtr的实例句柄调用LoadLibrary =(USER32);
        callbackDelegate =新keyboardHookProc(HOOKPROC);
        HHOOK = SetWindowsHookEx函数(WH_KEYBOARD_LL,callbackDelegate,实​​例句柄,0);
        如果(HHOOK == IntPtr.Zero)抛出新Win32Exception();
    }    公共无效脱钩()
    {
        如果(callbackDelegate == NULL)回报;
        布尔OK = UnhookWindowsHookEx(HHOOK);
        如果抛出新Win32Exception()(好!);
        callbackDelegate = NULL;
    }

没有必要的PInvoke FreeLibrary则,user32.dll中总是加载,直到你的程序终止。

I'm using a global keyboard hook class. This class allows to check if keyboard key pressed anywhere. And after some time I'm having an error:

        **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.

Here is globalkeyboardHook class:

        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<Keys> HookedKeys = new List<Keys>();

        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

Any ideas how to fix it? The program works well, but after some time the program freezes ant I get this error.

解决方案

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

There's your problem. You are relying on C# syntax sugar to have it automatically create a delegate object to hookProc. Actual code generation look like this:

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

There's only one reference to the delegate object, $temp. But it is local variable and disappears as soon as your hook() method stops executing and returns. The garbage collector is otherwise powerless to see that Windows has a 'reference' to it as well, it cannot probe unmanaged code for references. So the next time the garbage collector runs, the delegate object gets destroyed. And that's a kaboom when Windows makes the hook callback. The built-in MDA detects the problem and generates the helpful diagnostic before the program crashes with an AccessViolation.

You will need to create an additional reference to the delegate object that survives long enough. You could use GCHandle for example. Or easier, just store a reference yourself so the garbage collector can always see the reference. Add a field to your class. Making it static is a sure-fire way to ensure the object can't be collected:

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

No need to pinvoke FreeLibrary, user32.dll is always loaded until your program terminates.

这篇关于CallbackOnCollectedDelegate在globalKeyboardHook检测的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆