2015-12-15 3 views
5

Как часть приложения для медиаплеера, над которым я работаю, я хочу подключить глобальные нажатия клавиш управления мультимедиа (играть, пропустить вперед, пропустить назад и т. Д.).C# Hook Global Keyboard Events - .net 4.0

Я искал около 2 часов, пытаясь найти решение, но я не смог найти тот, который работал. Я нашел несколько ответов Stackoverflow о том же, но никто из них не работал.

Я попробовал пакет NuGet MouseKeyHook, но он никогда не выпустит событие. Я также попробовал пакет FMUtils.KeyboardHook, но произошло то же самое, за исключением того, что он был напечатан на консоли, что он закрыл крючок сразу после его запуска - и я понятия не имею, почему, событие после просмотра исходного кода.

Я попытался получить этот проект кодекса http://www.codeproject.com/Articles/18638/Using-Window-Messages-to-Implement-Global-System-H, но я даже не смог запустить демоверсию, обе демо просто бросили странные ошибки, которые я не мог проследить.

Мой вопрос в том, что известно, как работать способ захвата клавиатурных прессов в .net 4.0, который я могу использовать для захвата нажатия клавиш, когда мое приложение Winforms не сфокусировано?

ответ

8

Вот код, который я использую для нескольких проектов за последние X лет. Должна работать без проблем (для любой версии .Net в Windows). Надеюсь, это поможет вам.

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

Тест приложение:

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

Это может сделать с некоторым кодом очистки, но я не беспокоили, как это работает.

+1

Спасибо! он работал :) –

+0

Великий :) thx: p – Soheyl

+0

Единственные крючки, которые работают с C#, являются низкими: WH_KEYBOARD_LL и WH_MOUSE_LL. Итак, не глобальный крючок из этого примера не будет работать. [Доверяйте мне] (https://github.com/Gh61/csharp-global-windows-hook) или [Trust Microsoft] (https://support.microsoft.com/en-us/help/318804/how- к посаженным-а-окнам-крючок-в-визуальному-с - нетто) – Gh61