EN
C# - program that registers hot keys under Windows (WinForms application)
10
points
In this article, we would like to show how to register hot keys in C# Windows Forms application.
.NET doesn't provide API that lets to handle keys pressed under operating system, so it is necessary to call WinAPI functions.

Practical example
In the solution, the invisible window is created only to be application able to handle the pressed keys from the operating system - some window is needed. In your project you can use HotKeyManager
class with other existing application window.
Project structure:
MainForm.cs
file:
using System;
using System.Windows.Forms;
namespace Example
{
public partial class MainForm: InvisibleForm
{
private HotKeyManager? _manager;
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
this._manager = new HotKeyManager(this);
this._manager.KeyHandled += Manager_KeyHandled;
this._manager.RegisterHotKey(Keys.Escape, Modifiers.None); // Escape
this._manager.RegisterHotKey(Keys.Space, Modifiers.Control); // Ctrl + Space
}
protected override void OnHandleDestroyed(EventArgs e)
{
base.OnHandleDestroyed(e);
if (this._manager != null)
this._manager.Dispose();
}
protected override void WndProc(ref Message message)
{
base.WndProc(ref message);
if (this._manager != null)
this._manager.ProcessMessage(ref message);
}
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);
}
}
}
InvisibleForm.cs
file:
using System;
using System.Drawing;
using System.Windows.Forms;
public class InvisibleForm: Form
{
public InvisibleForm()
{
this.ShowInTaskbar = false;
this.FormBorderStyle = FormBorderStyle.None;
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
this.Size = Size.Empty;
}
}
Program.cs
file:
using System;
using System.Windows.Forms;
namespace Example
{
internal static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
// put common WinAPI configuration here ...
Application.Run(new MainForm());
}
}
}
HotKeyManager.cs
file:
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Windows.Forms;
[Flags]
public enum Modifiers
{
None = 0x0000,
Alt = 0x0001,
Control = 0x0002,
Shift = 0x0004,
Win = 0x0008,
NoRepeat = 0x4000
}
public class HotKeyManager : IDisposable
{
private int _id = 0;
private IntPtr _handle;
private HashSet<int> _ids = new HashSet<int>();
private bool _disposed = false;
public event EventHandler<HotKeyEventArgs>? KeyHandled;
public HotKeyManager(ContainerControl control)
{
this._handle = control.Handle;
}
~HotKeyManager()
{
this.Dispose(false);
}
/// <summary>
/// Registers hot key.
/// </summary>
/// <param name="key">registered key code</param>
/// <param name="modifiers">registered key modifiers (Ctrl, Alt, Shift, etc.)</param>
/// <returns>registered hot key id</returns>
/// <exception cref="ObjectDisposedException">when ovject was disposed</exception>
public int RegisterHotKey(Keys key, Modifiers modifiers)
{
if (this._disposed)
throw new ObjectDisposedException("Hot key manager has been destroyed.");
int id = (++this._id);
if (RegisterHotKey(this._handle, id, (uint)modifiers, (uint)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._handle, id))
this._ids.Remove(id);
}
public void ProcessMessage(ref Message message)
{
if (message.Msg == 0x312) // WinAPI WM_HOTKEY
{
long code = (long)message.LParam;
uint key = this.GetKey(code);
uint modifiers = this.GetModifiers(code);
this.KeyHandled?.Invoke(this, new HotKeyEventArgs((Keys)key, (Modifiers)modifiers));
}
}
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)
{
foreach (int id in this._ids)
UnregisterHotKey(this._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 Keys Key { get; }
public Modifiers Modifiers { get; }
public HotKeyEventArgs(Keys key, Modifiers modifiers)
{
this.Key = key;
this.Modifiers = modifiers;
}
}