Register hotkey in system for WPF application

    • .NET
    • C#
    • WPF
    • Hotkey
  • modified:
  • reading: 3 minutes

Couple days ago I got a question about how to register hotkey in Windows for WPF application. I remembered that one year ago I was solving the same problem in WinForms application, I was registering hot keys for my application, it was Vista Keys Extender project. I knew that my project worked, so I suggested author of question to use code of my project to solve his problem. But as we learned later in WPF message handle mechanism different from WinForms. So I started to find solution for WPF application. I copied my old code from my old project and started to rewrite it step-by-step.

First off all we need to import WinAPI methods:

``` internal class HotKeyWinApi { public const int WmHotKey = 0x0312; [DllImport("user32.dll", SetLastError = true)] public static extern bool RegisterHotKey(IntPtr hWnd, int id, ModifierKeys fsModifiers, Keys vk); [DllImport("user32.dll", SetLastError = true)] public static extern bool UnregisterHotKey(IntPtr hWnd, int id); } ```

In Vista Keys Extender project I implemented own enum KeyModifiers, but in WPF I don’t need to do this, because it has System.Windows.Input.ModifierKeys, which equals to my own enum. Also in my old project I used System.Windows.Forms.Keys, but in WPF enum System.Windows.Input.Key different, it has other values. I didn’t want to set reference from my new WPF project to assembly System.Windows.Forms, because I need just one enum. So I copied this enum from assembly to my new WPF project. Ok, so I did all preparations and now I need to realize main class HotKey:

``` public sealed class HotKey : IDisposable { public event Action HotKeyPressed; private readonly int _id; private bool _isKeyRegistered; readonly IntPtr _handle; public HotKey(ModifierKeys modifierKeys, Keys key, Window window) : this (modifierKeys, key, new WindowInteropHelper(window)) { Contract.Requires(window != null); } public HotKey(ModifierKeys modifierKeys, Keys key, WindowInteropHelper window) : this(modifierKeys, key, window.Handle) { Contract.Requires(window != null); } public HotKey(ModifierKeys modifierKeys, Keys key, IntPtr windowHandle) { Contract.Requires(modifierKeys != ModifierKeys.None || key != Keys.None); Contract.Requires(windowHandle != IntPtr.Zero); Key = key; KeyModifier = modifierKeys; _id = GetHashCode(); _handle = windowHandle; RegisterHotKey(); ComponentDispatcher.ThreadPreprocessMessage += ThreadPreprocessMessageMethod; } ~HotKey() { Dispose(); } public Keys Key { get; private set; } public ModifierKeys KeyModifier { get; private set; } public void RegisterHotKey() { if (Key == Keys.None) return; if (_isKeyRegistered) UnregisterHotKey(); _isKeyRegistered = HotKeyWinApi.RegisterHotKey(_handle, _id, KeyModifier, Key); if (!_isKeyRegistered) throw new ApplicationException("Hotkey already in use"); } public void UnregisterHotKey() { _isKeyRegistered = !HotKeyWinApi.UnregisterHotKey(_handle, _id); } public void Dispose() { ComponentDispatcher.ThreadPreprocessMessage -= ThreadPreprocessMessageMethod; UnregisterHotKey(); } private void ThreadPreprocessMessageMethod(ref MSG msg, ref bool handled) { if (!handled) { if (msg.message == HotKeyWinApi.WmHotKey && (int)(msg.wParam) == _id) { OnHotKeyPressed(); handled = true; } } } private void OnHotKeyPressed() { if (HotKeyPressed != null) HotKeyPressed(this); } } ```

And small sample of using:

``` public partial class MainWindow : Window { private HotKey _hotkey; public MainWindow() { InitializeComponent(); Loaded += (s, e) => { _hotkey = new HotKey(ModifierKeys.Windows | ModifierKeys.Alt, Keys.Left, this); _hotkey.HotKeyPressed += (k) => Console.Beep(); }; } } ```

You can look and download source code of this sample from my public SVN repository at assembla.

See Also