Languages
[Edit]
EN

C# - program that registers hot keys under Windows (WPF application)

4 points
Created by:
Barmar
338

In this article, we would like to show how to register hot keys in C# WPF application under Windows.

.NET doesn't provide API that lets to handle keys pressed under operating system, so it is necessary to call WinAPI functions.

Registered Ctrl+Space and Escape hot keys handled in Windows (WPF application).
Registered Ctrl+Space and Escape hot keys handled in Windows (WPF application).

Practical example

In the solution, the invisible window is created only to be application able to handle the pressed keys in the operating system - some window is needed. In your project you can use HotKeyManager class with other existing application window.

Project structure:

 

MainWindow.xaml file:

<Window x:Class="Example.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Example"
        mc:Ignorable="d"
        Title="MainWindow"
        Height="0"
        Width="0"
        MaxWidth="0"
        MaxHeight="0"
        WindowStyle="None"
        ResizeMode="NoResize"
        ShowInTaskbar="False"
        Loaded="Window_Loaded"
        Unloaded="Window_Unloaded" />

 

MainWindow.xaml.cs file:

using System.Windows;

namespace Example
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private HotKeyManager? _manager;

        public MainWindow()
        {
            this.InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            this._manager = new HotKeyManager(this);

            this._manager.KeyHandled += this.Manager_KeyHandled;

            this._manager.RegisterHotKey(0x1B, 0x0000);  // Escape         (in dec: 27)
            this._manager.RegisterHotKey(0x20, 0x0002);  // Ctrl + Space   (in dec: 2 and 32)
        }

        private void Window_Unloaded(object sender, RoutedEventArgs e)
        {
            if (this._manager != null)
                this._manager.Dispose();
        }

        private void Manager_KeyHandled(object? sender, HotKeyEventArgs e)
        {
            // you can handle yours cases here by checking e.Key and e.Modifiers

            MessageBox.Show("Key: " + e.Key + "  Modifiers: " + e.Modifiers);
        }
    }
}

 

HotKeyManager.cs file:

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;

public class HotKeyManager: IDisposable
{
    private int _id = 0;

    private HwndSource _source;
    private HashSet<int> _ids = new HashSet<int>();

    private bool _disposed = false;

    public event EventHandler<HotKeyEventArgs>? KeyHandled;

    public HotKeyManager(Window window)
    {
        this._source = (HwndSource)PresentationSource.FromVisual(window);

        if (this._source == null)
            throw new InvalidOperationException("Window doesn't have created handle yet.");
            
        this._source.AddHook(this.WndProc);
    }

    ~HotKeyManager()
    {
        this.Dispose(false);
    }

    /// <summary>
    /// Registers hot key.
    /// </summary>
    /// <param name="key">
    ///     registered key code
    ///     Key codes:
    ///       https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
    ///       e.g.
    ///       [Constant]   [Value]  [Description]
    ///        VK_SHIFT     0x10     SHIFT key
    ///        VK_CONTROL   0x11     CTRL key
    ///        VK_MENU      0x12     ALT key
    ///        VK_TAB       0x09     TAB key
    ///        VK_SPACE     0x20     SPACEBAR
    ///        etc.
    /// </param>
    /// <param name="modifiers">
    ///     registered key modifiers (Ctrl, Alt, Shift, etc.)
    ///     Key modifiers:
    ///       https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-registerhotkey
    ///       [Constant]     [Value]
    ///        ALT_KEY        0x0001
    ///        CONTROL_KEY    0x0002
    ///        SHIFT_KEY      0x0004
    ///        WIN_KEY        0x0008
    ///        NOREPEAT_KEY   0x4000
    /// </param>
    /// <returns>registered hot key id</returns>
    /// <exception cref="ObjectDisposedException">when ovject was disposed</exception>
    public int RegisterHotKey(uint key, uint modifiers)
    {
        if (this._disposed)
            throw new ObjectDisposedException("Hot key manager has been destroyed.");

        int id = (++this._id);

        if (RegisterHotKey(this._source.Handle, id, modifiers, key))
        {
            this._ids.Add(id);

            return id;
        }

        return -1; // when it is impossible to register hot key/keys
    }

    /// <summary>
    /// Unregisters hot key.
    /// </summary>
    /// <param name="id">registered hot key id</param>
    /// <exception cref="ObjectDisposedException">when object was disposed</exception>
    public void UnregisterHotKey(int id)
    {
        if (this._disposed)
            throw new ObjectDisposedException("Hot key manager has been destroyed.");

        if (UnregisterHotKey(this._source.Handle, id))
            this._ids.Remove(id);
    }

    private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
    {
        handled = false;

        if (msg == 0x312) // WinAPI WM_HOTKEY
        {
            long code = (long) lParam;

            uint key = this.GetKey(code);
            uint modifiers = this.GetModifiers(code);

            this.KeyHandled?.Invoke(this, new HotKeyEventArgs(key, modifiers));
        }
             
        return IntPtr.Zero;
    }

    private uint GetKey(long code)
    {
        return 0xFFFFu & (uint)(code >> 16);
    }

    private uint GetModifiers(long code)
    {
        return 0xFFFFu & (uint)(code >> 0);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!this._disposed)
        {
            if (disposing)
            {
                this._source.RemoveHook(this.WndProc);
                this._source.Dispose();
            }

            foreach (int id in this._ids)
                UnregisterHotKey(this._source.Handle, id);

            this._disposed = true;
        }
    }

    public void Dispose()
    {
        this.Dispose(true);

        GC.SuppressFinalize(this);
    }

    [DllImport("user32", SetLastError = true)]
    private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);

    [DllImport("user32", SetLastError = true)]
    private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
}

public class HotKeyEventArgs: EventArgs
{
    public uint Key { get; }
    public uint Modifiers { get; }

    public HotKeyEventArgs(uint key, uint modifiers)
    {
        this.Key = key;
        this.Modifiers = modifiers;
    }
}

 

See also

  1. C# - program that registers hot keys under Windows (WinForms application)

References

  1. Implement a Dispose method - Microsoft Docs

Alternative titles

  1. C# - handle keys pressed in Windows (WPF application)
  2. C# - register hot keys under Windows (WPF application)
  3. C# - program that adds hot keys in Windows (WPF application)
  4. C# - monitor pressed operating system keys (WPF application under Windows)
  5. C# - global keyboard shortcuts under Windows (WPF application under Windows)
  6. C# - program that registers hotkeys under Windows (WPF application)
Donate to Dirask
Our content is created by volunteers - like Wikipedia. If you think, the things we do are good, donate us. Thanks!
Join to our subscribers to be up to date with content, news and offers.
Native Advertising
🚀
Get your tech brand or product in front of software developers.
For more information Contact us
Dirask - we help you to
solve coding problems.
Ask question.

❤️💻 🙂

Join