【问题标题】:Managed Debugging Assistant 'CallbackOnCollectedDelegate' : C# windows form托管调试助手“CallbackOnCollectedDelegate”:C# windows 窗体
【发布时间】:2019-01-28 09:41:34
【问题描述】:

我正在开发一个多选考试系统,需要禁用所有键盘并只需使用鼠标单击,但我遇到了这个问题

对类型为垃圾收集的委托进行了回调 'UI!UI.Forms.frmExamHome+LowLevelKeyboardProcDelegate::Invoke'。这 可能导致应用程序崩溃、损坏和数据丢失。通过时 委托给非托管代码,它们必须由托管保持活动状态 应用程序,直到保证它们永远不会被调用。'

我的代码:

void wniDisable()
        {
           intLLKey = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, System.Runtime.InteropServices.Marshal.GetHINSTANCE(System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0]).ToInt32(), 0);

        }
        [DllImport("user32", EntryPoint = "SetWindowsHookExA", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
        public static extern int SetWindowsHookEx(int idHook,  LowLevelKeyboardProcDelegate lpfn, int hMod, int dwThreadId);
        [DllImport("user32", EntryPoint = "UnhookWindowsHookEx", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
        public static extern int UnhookWindowsHookEx(int hHook);
        public delegate int LowLevelKeyboardProcDelegate(int nCode, int wParam, ref KBDLLHOOKSTRUCT lParam);
        [DllImport("user32", EntryPoint = "CallNextHookEx", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
        public static extern int CallNextHookEx(int hHook, int nCode, int wParam, ref KBDLLHOOKSTRUCT lParam);
        public const int WH_KEYBOARD_LL = 13;

        /*code needed to disable start menu*/
        [DllImport("user32.dll")]
        private static extern int FindWindow(string className, string windowText);
        [DllImport("user32.dll")]
        private static extern int ShowWindow(int hwnd, int command);

        private const int SW_HIDE = 0;
        private const int SW_SHOW = 1;
        public struct KBDLLHOOKSTRUCT
        {
            public int vkCode;
            public int scanCode;
            public int flags;
            public int time;
            public int dwExtraInfo;
        }
        public static int intLLKey;

        public int LowLevelKeyboardProc(int nCode, int wParam, ref KBDLLHOOKSTRUCT lParam)
        {
            bool blnEat = false;

            switch (wParam)
            {
                case 256:
                case 257:
                case 260:
                case 261:
                    //Alt+Tab, Alt+Esc, Ctrl+Esc, Windows Key,
                    blnEat = ((lParam.vkCode == 9) && (lParam.flags == 32)) | ((lParam.vkCode == 27) && (lParam.flags == 32)) | ((lParam.vkCode == 27) && (lParam.flags == 0)) | ((lParam.vkCode == 91) && (lParam.flags == 1)) | ((lParam.vkCode == 92) && (lParam.flags == 1)) | ((lParam.vkCode == 73) && (lParam.flags == 0));
                    break;
            }

            if (blnEat == true)
            {
                return 1;
            }
            else
            {
                return CallNextHookEx(0, nCode, wParam, ref lParam);
            }
        }
        public void KillStartMenu()
        {
            int hwnd = FindWindow("Shell_TrayWnd", "");
            ShowWindow(hwnd, SW_HIDE);
        }

【问题讨论】:

标签: c# .net windows winforms


【解决方案1】:

您需要将钩子委托存储为类成员,否则委托实例将是方法本地的,并在您离开方法后被垃圾收集。

查看https://github.com/Alois-xx/etwcontroller/blob/master/ETWController/Hooking/Hooker.cs

用于工作样本。

   class Hooker : IDisposable
    {
        int MouseHookHandle;
        int KeyboardHookHandle;
        HookProc MouseHookGCRootedDelegate;
        HookProc KeyboardHookGCRootedDelegate;
..
    public Hooker()
        {
           // HookEvents.RegisterItself();
            MouseHookGCRootedDelegate = MouseHook;
            KeyboardHookGCRootedDelegate = KeyboardHook;
        }

        void HookKeyboard(bool bHook)
        {
            if (KeyboardHookHandle == 0 && bHook)
            {
                using (var mainMod = Process.GetCurrentProcess().MainModule)
                    KeyboardHookHandle = HookNativeDefinitions.SetWindowsHookEx(HookNativeDefinitions.WH_KEYBOARD_LL, KeyboardHookGCRootedDelegate, HookNativeDefinitions.GetModuleHandle(mainMod.ModuleName), 0);

                //If the SetWindowsHookEx function fails.
                if (KeyboardHookHandle == 0)
                {
                    System.Windows.MessageBox.Show("SetWindowsHookEx Failed " + new Win32Exception(Marshal.GetLastWin32Error()));
                    return;
                }
            }
            else if(bHook == false)
            {
                Debug.Print("Unhook keyboard");
                HookNativeDefinitions.UnhookWindowsHookEx(KeyboardHookHandle);
                KeyboardHookHandle = 0;
            }

        }

【讨论】:

  • 代码是用windows形式写的,我应该用上面的代码编辑表单代码或创建类
  • @AmaniCodes:从干净代码的角度来看,将挂钩提取到单独的类中会更干净。 SRP 原则(单一职责原则)适用于此。一门课应该只有一个目的。
  • 我仍然需要帮助来修复此错误,请提供任何帮助
  • 您可以将我的课程复制到您的项目中。它应该可以工作并且几乎是独立的。
  • @Rans:.cs 文件中没有 disposing 参数。不确定你在看什么。无论如何,当您实现 IDisposable 接口时,代码分析器会以这种方式实现它,以便您稍后添加一个仅清理非托管资源的终结器。那是在 .NET 的开始,没有 SafeHandles 存在,许多类可以携带非托管资源。这是一个糟糕的模式,并且被放弃了,转而支持包装从 CriticalFinalizerObject 派生的非托管资源的小类。
猜你喜欢
  • 1970-01-01
  • 2018-02-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-14
相关资源
最近更新 更多