2015-12-15 22 views
5

Come parte di un'applicazione di media player su cui sto lavorando, voglio agganciare i tasti di pressione globali per i tasti di controllo dei media (riproduci, salta avanti, salta indietro, ecc.).C# Hook Global Keyboard Events - .net 4.0

Ho cercato per circa 2 ore ora di cercare una soluzione, ma non sono riuscito a trovarne uno funzionante. Ho trovato diverse risposte Stackoverflow circa la stessa cosa, ma nessuno di loro ha funzionato.

Ho provato il pacchetto MouseKeyHook NuGet, ma non avrebbe mai attivato l'evento. Ho provato anche il pacchetto FMUtils.KeyboardHook, ma la stessa cosa è accaduta, tranne che nella console è stato stampato che ha spento il gancio subito dopo averlo avviato - e non ho idea del perché, evento dopo aver guardato il codice sorgente.

Ho tentato di ottenere questo progetto codeproject http://www.codeproject.com/Articles/18638/Using-Window-Messages-to-Implement-Global-System-H ma non riuscivo nemmeno a eseguire la demo, entrambi i demo hanno appena generato strani errori che non riuscivo a rintracciare.

La mia domanda è, che cosa è un modo noto per catturare le tastiere in .net 4.0 che posso utilizzare per acquisire le pressioni della tastiera quando l'applicazione Winforms non è a fuoco?

risposta

8

Ecco il codice che ho utilizzato per diversi progetti negli ultimi X anni. Dovrebbe funzionare senza problemi (per qualsiasi versione .Net su Windows). Spero che ti aiuti.

public class KeyboardHook : IDisposable 
{ 
    bool Global = false; 

    public delegate void LocalKeyEventHandler(Keys key, bool Shift, bool Ctrl, bool Alt); 
    public event LocalKeyEventHandler KeyDown; 
    public event LocalKeyEventHandler KeyUp; 

    public delegate int CallbackDelegate(int Code, int W, int L); 

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] 
    public struct KBDLLHookStruct 
    { 
     public Int32 vkCode; 
     public Int32 scanCode; 
     public Int32 flags; 
     public Int32 time; 
     public Int32 dwExtraInfo; 
    } 

    [DllImport("user32", CallingConvention = CallingConvention.StdCall)] 
    private static extern int SetWindowsHookEx(HookType idHook, CallbackDelegate lpfn, int hInstance, int threadId); 

    [DllImport("user32", CallingConvention = CallingConvention.StdCall)] 
    private static extern bool UnhookWindowsHookEx(int idHook); 

    [DllImport("user32", CallingConvention = CallingConvention.StdCall)] 
    private static extern int CallNextHookEx(int idHook, int nCode, int wParam, int lParam); 

    [DllImport("kernel32.dll", CallingConvention = CallingConvention.StdCall)] 
    private static extern int GetCurrentThreadId(); 

    public enum HookType : int 
    { 
     WH_JOURNALRECORD = 0, 
     WH_JOURNALPLAYBACK = 1, 
     WH_KEYBOARD = 2, 
     WH_GETMESSAGE = 3, 
     WH_CALLWNDPROC = 4, 
     WH_CBT = 5, 
     WH_SYSMSGFILTER = 6, 
     WH_MOUSE = 7, 
     WH_HARDWARE = 8, 
     WH_DEBUG = 9, 
     WH_SHELL = 10, 
     WH_FOREGROUNDIDLE = 11, 
     WH_CALLWNDPROCRET = 12, 
     WH_KEYBOARD_LL = 13, 
     WH_MOUSE_LL = 14 
    } 

    private int HookID = 0; 
    CallbackDelegate TheHookCB = null; 

    //Start hook 
    public KeyboardHook(bool Global) 
    { 
     this.Global = Global; 
     TheHookCB = new CallbackDelegate(KeybHookProc); 
     if (Global) 
     { 
      HookID = SetWindowsHookEx(HookType.WH_KEYBOARD_LL, TheHookCB, 
       0, //0 for local hook. eller hwnd til user32 for global 
       0); //0 for global hook. eller thread for hooken 
     } 
     else 
     { 
      HookID = SetWindowsHookEx(HookType.WH_KEYBOARD, TheHookCB, 
       0, //0 for local hook. or hwnd to user32 for global 
       GetCurrentThreadId()); //0 for global hook. or thread for the hook 
     } 
    } 

    bool IsFinalized = false; 
    ~KeyboardHook() 
    { 
     if (!IsFinalized) 
     { 
      UnhookWindowsHookEx(HookID); 
      IsFinalized = true; 
     } 
    } 
    public void Dispose() 
    { 
     if (!IsFinalized) 
     { 
      UnhookWindowsHookEx(HookID); 
      IsFinalized = true; 
     } 
    } 

    //The listener that will trigger events 
    private int KeybHookProc(int Code, int W, int L) 
    { 
     KBDLLHookStruct LS = new KBDLLHookStruct(); 
     if (Code < 0) 
     { 
      return CallNextHookEx(HookID, Code, W, L); 
     } 
     try 
     { 
      if (!Global) 
      { 
       if (Code == 3) 
       { 
        IntPtr ptr = IntPtr.Zero; 

        int keydownup = L >> 30; 
        if (keydownup == 0) 
        { 
         if (KeyDown != null) KeyDown((Keys)W, GetShiftPressed(), GetCtrlPressed(), GetAltPressed()); 
        } 
        if (keydownup == -1) 
        { 
         if (KeyUp != null) KeyUp((Keys)W, GetShiftPressed(), GetCtrlPressed(), GetAltPressed()); 
        } 
        //System.Diagnostics.Debug.WriteLine("Down: " + (Keys)W); 
       } 
      } 
      else 
      { 
       KeyEvents kEvent = (KeyEvents)W; 

       Int32 vkCode = Marshal.ReadInt32((IntPtr)L); //Leser vkCode som er de første 32 bits hvor L peker. 

       if (kEvent != KeyEvents.KeyDown && kEvent != KeyEvents.KeyUp && kEvent != KeyEvents.SKeyDown && kEvent != KeyEvents.SKeyUp) 
       { 
       } 
       if (kEvent == KeyEvents.KeyDown || kEvent == KeyEvents.SKeyDown) 
       { 
        if (KeyDown != null) KeyDown((Keys)vkCode, GetShiftPressed(), GetCtrlPressed(), GetAltPressed()); 
       } 
       if (kEvent == KeyEvents.KeyUp || kEvent == KeyEvents.SKeyUp) 
       { 
        if (KeyUp != null) KeyUp((Keys)vkCode, GetShiftPressed(), GetCtrlPressed(), GetAltPressed()); 
       } 
      } 
     } 
     catch (Exception) 
     { 
      //Ignore all errors... 
     } 

     return CallNextHookEx(HookID, Code, W, L); 

    } 

    public enum KeyEvents 
    { 
     KeyDown = 0x0100, 
     KeyUp = 0x0101, 
     SKeyDown = 0x0104, 
     SKeyUp = 0x0105 
    } 

    [DllImport("user32.dll")] 
    static public extern short GetKeyState(System.Windows.Forms.Keys nVirtKey); 

    public static bool GetCapslock() 
    { 
     return Convert.ToBoolean(GetKeyState(System.Windows.Forms.Keys.CapsLock)) & true; 
    } 
    public static bool GetNumlock() 
    { 
     return Convert.ToBoolean(GetKeyState(System.Windows.Forms.Keys.NumLock)) & true; 
    } 
    public static bool GetScrollLock() 
    { 
     return Convert.ToBoolean(GetKeyState(System.Windows.Forms.Keys.Scroll)) & true; 
    } 
    public static bool GetShiftPressed() 
    { 
     int state = GetKeyState(System.Windows.Forms.Keys.ShiftKey); 
     if (state > 1 || state < -1) return true; 
     return false; 
    } 
    public static bool GetCtrlPressed() 
    { 
     int state = GetKeyState(System.Windows.Forms.Keys.ControlKey); 
     if (state > 1 || state < -1) return true; 
     return false; 
    } 
    public static bool GetAltPressed() 
    { 
     int state = GetKeyState(System.Windows.Forms.Keys.Menu); 
     if (state > 1 || state < -1) return true; 
     return false; 
    } 
} 

Test App:

static class Program 
{ 
    [STAThread] 
    static void Main() 
    { 
     var kh = new KeyboardHook(true); 
     kh.KeyDown += Kh_KeyDown; 
     Application.Run(); 
    } 

    private static void Kh_KeyDown(Keys key, bool Shift, bool Ctrl, bool Alt) 
    { 
     Debug.WriteLine("The Key: " + key); 
    } 
} 

Si potrebbe fare con un po 'di pulizia del codice, ma non l'ho disturbato come funziona.

+1

Grazie! ha funzionato :) –

+0

Great :) thx: p – Soheyl

+0

Gli unici hook che stanno lavorando con C# sono di basso livello: WH_KEYBOARD_LL e WH_MOUSE_LL. Quindi, l'hook non globale da questo esempio non funzionerà. [Trust me] (https://github.com/Gh61/csharp-global-windows-hook) o [Trust Microsoft] (https://support.microsoft.com/en-us/help/318804/how- a-set-a-finestre-hook-in-visual-c - net) – Gh61