2015-12-15 15 views
5

W ramach aplikacji odtwarzacza multimediów, nad którą pracuję, chcę włączyć globalne naciśnięcia klawiszy sterujących multimediami (odtwarzanie, przeskakiwanie do przodu, pomijanie, itp.).C# Hook Global Keyboard Events - .net 4.0

Szukałem około 2 godzin, próbując znaleźć rozwiązanie - ale nie mogłem znaleźć takiego, które działało. Znalazłem kilka odpowiedzi Stackoverflow na ten sam temat, ale żaden z nich nie działał.

Wypróbowałem pakiet NuGet MouseKeyHook, ale nigdy nie wystrzeliłbym tego wydarzenia. Próbowałem też pakietu FMUtils.KeyboardHook, ale to samo się stało, z wyjątkiem tego, że wydrukowano go na konsoli, że wyłączył hak zaraz po uruchomieniu - i nie mam pojęcia dlaczego, zdarzenie po obejrzeniu kodu źródłowego.

Próbowałem uzyskać ten projekt codeprojekt http://www.codeproject.com/Articles/18638/Using-Window-Messages-to-Implement-Global-System-H, ale nie mogłem nawet uruchomić demo, oba dema właśnie rzuciły dziwne błędy, których nie mogłem odczytać.

Moje pytanie brzmi, jaki jest znany sposób przechwytywania naciskania klawiatury w .net 4.0, którego mogę użyć do przechwytywania naciśnięć klawiaturowych, gdy moja aplikacja WinForm nie jest ustawiona?

Odpowiedz

8

Oto kod, którego używam dla kilku projektów w ciągu ostatnich X lat. Powinien działać bez problemów (dla dowolnej wersji .Net w systemie Windows). Mam nadzieję, że ci to pomoże.

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

aplikacja testowa:

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

To może zrobić z niektórych czyszczenia kodu, ale nie przeszkadza jak to działa.

+1

Dzięki! zadziałało :) –

+0

Świetne :) thx: p – Soheyl

+0

Jedynymi hakami, które działają z C# są niski poziom: WH_KEYBOARD_LL i WH_MOUSE_LL. Haczyk nieglobalny z tego przykładu nie zadziała. [Zaufaj mi] (https://github.com/Gh61/csharp-global-windows-hook) lub [Zaufaj Microsoft] (https://support.microsoft.com/en-us/help/318804/how- ustawianie-okna-hook-in-visual-c-net) – Gh61