c# – CallbackOnCollectedDelegate在globalKeyboardHook被检测到

前端之家收集整理的这篇文章主要介绍了c# – CallbackOnCollectedDelegate在globalKeyboardHook被检测到前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我正在使用全局键盘钩子类.该类允许检查键盘按键是否按下.过了一段时间后,我发生错误
**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.

这里是globalkeyboardHook类:

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,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,ref keyboardHookStruct lParam);

        [DllImport("kernel32.dll")]
        static extern IntPtr LoadLibrary(string lpFileName);
        #endregion

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

解决方法

hhook = SetWindowsHookEx(WH_KEYBOARD_LL,0);

有你的问题你依靠C#语法糖让它自动创建一个委托对象到hookProc.实际代码生成如下所示:

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

对代理对象只有一个引用$temp.但是,一旦你的hook()方法停止执行并返回,它就是局部变量并消失.垃圾收集器是无能为力的,看到Windows也有一个“引用”,它不能探测非托管代码的引用.所以下次垃圾收集器运行时,委托对象就会被销毁.当Windows使钩子回调时,这是一个kaboom.内置的MDA检测问题,并在程序使用AccessViolation崩溃之前生成有用的诊断.

您将需要创建一个对代理对象的额外引用,该对象可以持续足够长时间.您可以使用GCHandle为例.或者更容易,只是自己存储一个引用,所以垃圾回收器总是可以看到引用.在你的班上添加一个字段.使其静态是确保无法收集对象的确定方式:

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

不需要固定FreeLibrary,user32.dll始终加载,直到程序终止.

猜你在找的C#相关文章