2013-03-21 5 views
0

Мне нужно захватить глобальные сообщения клавиатуры, поэтому я использую SetWindowsHookEx() с WH_KEYBOARD_LL. Но он работает только тогда, когда приложение находится в фокусе и не вызывает Callback во всем мире. Почти тот же код отлично работает с 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

+0

Yeap, мне нужно будет записывать каждый вход, так как это будет «рекордер». – UnknitSplash

+0

Если это код вашей основной формы, то разве вы не должны регистрировать обработчик событий где-нибудь? – Guido

ответ

0

Проблема была в функции «отладки». Форма tempForm = Form.ActiveForm;

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

Аналогичные изменения должны быть внесены и в другие подписи. Несмотря на то, что у самого microsoft есть ресурсы, которые показывают SetwindowsHookEx с int как возвращаемым типом (https://support.microsoft.com/en-us/kb/318804), вы никогда не должны беззаботно подставлять «IntPtr» другим типом. В этом случае «int» эквивалентно «IntPtr» только в 32-битных ОС. На 64-битных платформах «IntPtr» имеет 64 бит, а «int» продолжает существовать только 32 бита, что открывает целую банку червей. Один из худших аспектов использования «int» заключается в том, что если возвращаемое значение SetWindowsHookEx не может поместиться в него, вы, скорее всего, окажетесь в искаженном int-handle (его редком, но не непостижимом).

Это означает, что если срок службы вашего приложения проходит мимо точки, в которую вы отцепляете/удаляете крючок (не для того, чтобы начать разговор с самого начала, чтобы начать с ...), тогда вы можете закончить замораживание мыши и/или клавиатуру до тех пор, пока не будет убит хост-процесс. Большинство людей просто перезагружают машину в этот момент и полностью утилизируют ваше приложение. Все это «зависание клавиатуры/мыши» происходит из-за того, что неподдерживаемый крючок должен доходить до сообщения-сообщения вашего приложения-хозяина, чего, очевидно, не может быть, поскольку компонент, к которому он принадлежал, вышел из строя во время удаления - wooohoo!

TL; Обратите внимание на сигнатуры используемых методов p/invoke и IntPtrs.