1

我需要捕获全局键盘消息,所以我使用 SetWindowsHookEx() 和 WH_KEYBOARD_LL。但它仅在应用程序处于焦点时有效,并且不会全局触发回调。几乎相同的代码适用于 mouse_LL(具有另一个结构等)请帮助!

public const int WH_KEYBOARD_LL = 13;
public const int VK_INSERT = 0x2D;
public delegate int HookProc(int nCode, IntPtr wParam, IntPtr lParam);
HookProc KeyboardHookProcedure;

[DllImport("user32.dll", CharSet = CharSet.Auto,
   CallingConvention = CallingConvention.StdCall)]
  public static extern int SetWindowsHookEx(int idHook, HookProc lpfn,
  IntPtr hInstance, int threadId);

[DllImport("user32.dll", CharSet = CharSet.Auto,
   CallingConvention = CallingConvention.StdCall)]
  public static extern bool UnhookWindowsHookEx(int idHook);

[DllImport("user32.dll", CharSet = CharSet.Auto,
   CallingConvention = CallingConvention.StdCall)]
  public static extern int CallNextHookEx(int idHook, int nCode,
  IntPtr wParam, IntPtr lParam);

[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
  private static extern IntPtr GetModuleHandle(string lpModuleName);

[StructLayout(LayoutKind.Sequential)]
  private struct KBDLLHOOKSTRUCT
  {
     public uint vkCode;
     public uint scanCode;
     public uint flags;
     public uint time;
     public IntPtr dxExtraInfo;
  }

private void SetHookKeyboard()
  {
     if (kHook == 0)
     {
        KeyboardHookLL();

        //If the SetWindowsHookEx function fails.
        if (kHook == 0)
        {
           MessageBox.Show("SetWindowsHookEx Failed");
           return;
        }
        button1.Text = "UnHook Windows Hook";
     }
     else
     {
        bool ret = UnhookWindowsHookEx(kHook);
        //If the UnhookWindowsHookEx function fails.
        if (ret == false)
        {
           MessageBox.Show("UnhookWindowsHookEx Failed");
           return;
        }
        kHook = 0;
        this.Text = "Keyboard Hook";
     }
  }

private void KeyboardHookLL()
  {
     KeyboardHookProcedure = new HookProc(MainForm.KeyboardHookProc);
     kHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookProcedure,   GetModuleHandle("user32"), 0);
  }

public static int KeyboardHookProc(int nCode, IntPtr wParam, IntPtr lParam)
  {
     KBDLLHOOKSTRUCT MyKeyboardHookStruct = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT));
     if (nCode < 0)
     {
        return CallNextHookEx(hHook, nCode, wParam, lParam);
     }
     else
     {
        Form tempForm = Form.ActiveForm;
        tempForm.Text = MyKeyboardHookStruct.vkCode.ToString();
        if (MyKeyboardHookStruct.vkCode == VK_INSERT)
        {
           MainForm.botAlive = false;
           MessageBox.Show(MainForm.botAlive.ToString());
        }
        return CallNextHookEx(hHook, nCode, wParam, lParam);
     }
  }
4

2 回答 2

1

这里归功于乔恩:

当您有句柄时,int vs IntPtr?

我知道这是一篇旧帖子,我有点离题了,但我注意到原始代码中有一个险恶的缺点,它可能会在适当的时候转过来咬你非常非常糟糕(我知道在我的情况下确实如此):

  [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
  public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);

应该更优选的是:

  [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
   static extern IntPtr SetWindowsHookEx(HookType hookType, HookProc lpfn, IntPtr hMod, uint dwThreadId);

其他签名也应该进行类似的更改。即使 microsoft 本身有资源显示 SetwindowsHookEx 以 int 作为返回类型 ( https://support.microsoft.com/en-us/kb/318804 ),您也不应该轻松地将 'IntPtr' 替换为其他类型。在这种情况下,'int' 仅在 32 位操作系统中等同于 'IntPtr'。在 64 位平台中,'IntPtr' 是 64 位,而 'int' 继续只有 32 位,这会打开一整罐蠕虫。使用 'int' 最糟糕的方面之一是,如果 SetWindowsHookEx 的返回值不适合它,那么您很可能最终会得到一个损坏的 int 句柄(它很少见,但并非不可想象)。

这意味着如果您的应用程序的生命周期超过了您取消挂钩/处置挂钩的时间点(并不是说取消挂钩调用将从...开始工作),那么您最终可能会完全冻结鼠标和/或键盘直到宿主进程被杀死。大多数人会在那时简单地重新启动机器并完全丢弃您的应用程序。所有这些“键盘/鼠标冻结”的事情发生是因为未处理的钩子需要到达主机应用程序的消息泵,这显然不可能发生,因为它所属的组件在处理过程中已经崩溃 - 哇哦!

TL;DR:请注意您正在使用的 p/invoke 方法的签名,尤其是 IntPtrs。

于 2016-03-23T20:43:55.103 回答
0

问题出在“调试”功能中。

Form tempForm = Form.ActiveForm;
于 2013-03-29T07:56:47.847 回答