using System; using System.Diagnostics; using System.Runtime.InteropServices; using System.Windows.Forms; namespace MediaBrowser.UI.UserInput { /// /// Provides a basic low-level keyboard listener /// Inspired by http://blogs.msdn.com/b/toub/archive/2006/05/03/589423.aspx /// Use the KeyDown event to listen for keys. /// Make sure to detach from the event when not needed. /// public static class KeyboardListener { #region KeyDown EventHandler /// /// The _ key down /// static volatile EventHandler _KeyDown; /// /// Fires whenever CurrentItem changes /// public static event EventHandler KeyDown { add { if (_KeyDown == null) { StartListening(); } _KeyDown += value; } remove { _KeyDown -= value; if (_KeyDown == null && _hookID != IntPtr.Zero) { StopListening(); } } } /// /// Raises the event. /// /// The instance containing the event data. private static void OnKeyDown(KeyEventArgs e) { e.SuppressKeyPress = false; if (_KeyDown != null) { // For now, don't async this // This will give listeners a chance to modify SuppressKeyPress if they want try { _KeyDown(null, e); } catch (Exception ex) { } } } #endregion /// /// The W h_ KEYBOAR d_ LL /// private const int WH_KEYBOARD_LL = 13; /// /// The W m_ KEYDOWN /// private const int WM_KEYDOWN = 0x0100; /// /// The W m_ SYSKEYDOWN /// private const int WM_SYSKEYDOWN = 0x0104; /// /// The _hook ID /// private static IntPtr _hookID = IntPtr.Zero; /// /// The _proc /// private static LowLevelKeyboardProc _proc = HookCallback; /// /// Starts the listening. /// private static void StartListening() { _hookID = SetHook(_proc); } /// /// Stops the listening. /// private static void StopListening() { UnhookWindowsHookEx(_hookID); _hookID = IntPtr.Zero; } /// /// Sets the hook. /// /// The proc. /// IntPtr. private static IntPtr SetHook(LowLevelKeyboardProc proc) { using (var curProcess = Process.GetCurrentProcess()) using (var curModule = curProcess.MainModule) { return SetWindowsHookEx(WH_KEYBOARD_LL, proc, GetModuleHandle(curModule.ModuleName), 0); } } /// /// Hooks the callback. /// /// The n code. /// The w param. /// The l param. /// IntPtr. private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) { var suppressKeyPress = false; if (nCode >= 0) { if (wParam == (IntPtr)WM_KEYDOWN || wParam == (IntPtr)WM_SYSKEYDOWN) { var vkCode = Marshal.ReadInt32(lParam); var keyData = (Keys)vkCode; var e = new KeyEventArgs(keyData); OnKeyDown(e); suppressKeyPress = e.SuppressKeyPress; } } if (suppressKeyPress) { return IntPtr.Zero; } return CallNextHookEx(_hookID, nCode, wParam, lParam); } /// /// Delegate LowLevelKeyboardProc /// /// The n code. /// The w param. /// The l param. /// IntPtr. private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam); #region Imports /// /// Sets the windows hook ex. /// /// The id hook. /// The LPFN. /// The h mod. /// The dw thread id. /// IntPtr. [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId); /// /// Unhooks the windows hook ex. /// /// The HHK. /// true if XXXX, false otherwise [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool UnhookWindowsHookEx(IntPtr hhk); /// /// Calls the next hook ex. /// /// The HHK. /// The n code. /// The w param. /// The l param. /// IntPtr. [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam); /// /// Gets the module handle. /// /// Name of the lp module. /// IntPtr. [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern IntPtr GetModuleHandle(string lpModuleName); #endregion } }