全局键盘钩

时间:2013-03-21 12:47:49

标签: keyboard-hook setwindowshookex

我需要捕获全局键盘消息,因此我将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);
     }
  }

2 个答案:

答案 0 :(得分:1)

此处有人向Jon致信:

int vs IntPtr when you have a handle?

我知道这是一个很老的帖子而且我有点离题了,但是我注意到原始代码中有一个险恶的缺点,它可以在适当的时候扭转你并且非常糟糕(我知道它在我的情况下做了):

  [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);

也应对其他签名进行类似的更改。 即使微软本身拥有显示SetwindowsHookEx并且int为返回类型(https://support.microsoft.com/en-us/kb/318804)的资源,您也不应该轻易地将'IntPtr'替换为其他类型。在这种情况下,'int'仅相当于32位操作系统中的'IntPtr'。在64位平台中,'IntPtr'是64位,而'int'只保持32位,这就打开了一大堆蠕虫。使用'int'的最糟糕的一个方面是,如果SetWindowsHookEx的返回值不适合它,那么你很可能最终会得到一个错误的int-handle(它很少但不可思议)。

这意味着,如果你的应用程序的生命周期延长到你取消挂钩/丢弃挂钩的程度(而不是unhook调用将开始工作......)那么你可能最终会冻结鼠标和/或键盘完全被移除,直到主机进程被终止。大多数人只需在此时重新启动计算机并完全处理您的应用程序。所有这些“键盘/鼠标冻结”的事情都发生了,因为未使用的钩子需要到达主机应用程序的消息泵,这显然不可能发生,因为它所属的组件在处理期间已经破灭 - 哇哇!

TL; DR:注意你正在使用的p / invoke方法的签名和特别是IntPtrs。

答案 1 :(得分:0)

问题在于“调试”功能。     表格tempForm = Form.ActiveForm;