2013-03-21 136 views
0

我需要捕获全局键盘消息,所以我使用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); 
    } 
    } 
+0

一般来说,使用热键而不是挂钩更安全。为什么你需要一个钩子而不是一个热键有没有特别的理由? – Guido 2013-03-21 12:52:08

+0

Yeap,我需要捕捉每一个输入,因为它将成为一个“记录器”。 – UnknitSplash 2013-03-21 12:59:51

+0

如果这是你的mainform的代码,那么你不应该注册事件处理程序吗? – Guido 2013-03-22 08:37:12

回答

0

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

1

幸得乔恩这里:

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

对其他签名也应做类似的更改。 虽然Microsoft本身具有以int形式显示SetwindowsHookEx作为返回类型的资源(https://support.microsoft.com/en-us/kb/318804),但您绝不应轻易用其他类型替代'IntPtr'。在这种情况下,'int'仅在32位操作系统中等同于'IntPtr'。在64位平台上,IntPtr是64位,而'int'只保留在32位上,从而打开一整罐蠕虫。使用'int'最糟糕的一个方面是,如果SetWindowsHookEx的返回值不能适应它,那么你很可能最终会出现一个错位的int句柄(它很少见但并不是不可思议的)。

这意味着如果您的应用程序的生命周期超过了您解除挂钩的位置(而不是解除挂钩的调用将始终工作以开始......),那么您最终可能会冻结鼠标和/或键盘,直到主机进程被终止。大多数人都会在这一刻重新启动机器,并完全处置您的应用程序。所有这些“键盘/鼠标冻结”的事情都会发生,因为未配置的钩子需要到达主机应用程序的消息泵,显然这不会发生,因为它所属的组件已经在处理过程中失效了 - wooohoo!

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