BlackWasp
Input / Output
.NET 1.1+

Registering System-Wide Hot Keys

Some Windows applications execute in the background and are activated only as required. To enhance such a program's usability it is useful to register a system-wide hot key. This allows the software to activate when a specific key combination is pressed.

RegisterHotKey API Function

It is a common requirement, when developing Windows Forms software, that an entire program or some element of it will execute in the background. Often, the user will need to interact with the application only occasionally. At this time they will click a system tray icon or press a special key combination to show the user interface. Two good examples of this are Microsoft Outlook, which may be minimised to the system tray, and Launchy, which has no user interface until summoned.

The .NET framework does not provide a simple manner in which to detect keypresses for a Windows Forms application that is not currently active. However, using Platform Invocation Services (P/Invoke), we can call a Windows API function that registers a system-wide hot key and links it to a form. Once registered, if the user presses the specified key, or combination of keys, a message will automatically be sent to the appropriate form. This message will be received even if the form is inactive.

Hot Key Sample

In this article we will create a simple Windows Forms program that includes registering a hot key. When the key is pressed, the program's form will be activated. To begin, create a new Windows Forms application. The project should include a single form by default. All of the code for the sample will be contained within this form.

Declaring the API Functions

As the code in this sample uses P/Invoke, we will be using attributes from the System.Runtime.InteropServices namespace. To keep the code as simple to read as possible, add the following using directive to the top of the form's code file.

using System.Runtime.InteropServices;

There are two API functions that should be used when registering system-wide hot keys. The first is used to set up the hot key and is named "RegisterHotKey". The second function is used to remove the registration when it is no longer required. This is named "UnregisterHotKey".

To declare the two functions, add the following code within the form class' code block.

[DllImport("user32.dll")]
private static extern bool RegisterHotKey(
    IntPtr hWnd, int id, int fsModifiers, int vlc);

[DllImport("user32.dll")]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);

The RegisterHotKey uses the following four parameters:

  • hWnd. The hWnd parameter is used to specify a handle to the window that should receive messages when the hot key is pressed. In the sample we will provide the handle of the form using its in-built Handle property.
  • id. Each hot key is registered with an identification number using this parameter. This value is unique for the specified window handle. If the id is already in use, the previous registration is replaced with a new version.
  • fsModifiers. Hot keys are almost always registered as a combination of keys so that they are less likely to interfere with the operation of other software. The fsModifiers parameter is used to specify one or more keys that must be pressed at the same time as the hot key. The modifiers can be Ctrl, Alt, Shift, the Windows key or any combination of these four keys.
  • vlc. The final parameter is used to provide the virtual key code for the hot key itself.

The function returns a Boolean value. If the process is successful, the value will be true. If it fails, the return value will be false. A failure usually indicates that the hot key combination is already in use or is reserved by the operating system.

The UnregisterHotKey uses the first two of the above parameters only. It does not need to be provided with the hot key or the modifiers, as the combination of handle and unique ID provides enough information to remove the registration.

Hot Key API Constants

When calling the RegisterHotKey function, the fsModifiers parameter is used to specify which keys must be used in conjunction with the hot key to trigger a message. In the rare instance that the hot key is to be used in isolation, you should pass zero to this parameter. In all other cases, you should pass a value that represents the combination of the four possible modifier keys that you wish to register.

To improve the readability and maintainability of the code, we will declare four constants within the form's class to represent the four modifier keys. To use more than one of the modifier keys, the appropriate constants can be combined using the bitwise OR operator.

To declare the four constants, add the code below to the form's class. This includes a fifth constant named "WM_HOTKEY". This constant holds a value that represents the type of message that is sent when a hot key is pressed. We will use this value when processing incoming messages to filter out other message types.

const int MOD_ALT = 0x1;
const int MOD_CONTROL = 0x2;
const int MOD_SHIFT = 0x4;
const int MOD_WIN = 0x8;
const int WM_HOTKEY = 0x312;

Registering the Hot Key

Now that we have added the Windows API functions and associated constants, we can register a hot key during the Load event of our form. We will register the keyboard combination of the Windows key and Q, so that whenever these two keys are pressed simultaneously a message will be sent to the form. We will add the code to react to the message later in the article.

To register the hot key, use the form designer to add the Load event to the form and then modify the event's code as shown below:

private void Form1_Load(object sender, EventArgs e)
{
    MessageBox.Show(RegisterHotKey(
        (IntPtr)Handle, 1, MOD_WIN, (int)Keys.Q).ToString());
}

You can see that the first parameter uses the handle of the form, cast as IntPtr, to specify the window that will receive the message. The unique ID for the registered key is 1. The last two parameters specify that the Windows key will be the modifier and that Q is the hot key. If this key combination is already in use on your system, or if you do not have a Windows key, modify these parameters to select different options. Note also that the return value of the function is being converted to a string and displayed in a message box. This will allow you to see whether the registration is successful or not. In production code you should obtain the return value and react appropriately to a registration failure.

Unregistering the Hot Key

It is important that any registered hot keys are unregistered when they are no longer required. For the sample, we will unregister the hot key in the FormClosing event. Add this event to the project and include the following code:

private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
    MessageBox.Show(UnregisterHotKey((IntPtr)Handle, 1).ToString());
}

Capturing the Hot Key Message

When the user presses the hot key, a message is sent to the window that was specified in the call to RegisterHotKey. In the sample code, the window is the only form in the project. To receive the message, you must override a method of the form. This method, named "WndProc", is used to receive all messages that are destined for the window.

The WndProc method defines a single parameter that contains the received message. This is encapsulated in an instance of the Message class from the System.Windows.Forms namespace. As the method receives many messages that are not related to registered hot keys, we must check that the type of any received message is correct. This is achieved by comparing the Msg property of the message to the WM_HOTKEY constant that was declared earlier. If they match, an appropriate action can be taken. Non-matching messages must not be lost so these are processed with a call to the base class' method.

To override the method, add the following code to the form's class. This changes the size of the form by setting the normal window state and activates the form to ready it for potential input.

protected override void WndProc(ref Message m)
{
    base.WndProc(ref m);

    if (m.Msg == WM_HOTKEY)
    {
        WindowState = FormWindowState.Normal;
        Activate();
    }
}

With the WndProc method overridden, execute the program. Try switching to another program or minimising the form before pressing the Windows key and Q simultaneously to see the form reappear and become active.

Adding a Second Hot Key

Sometimes you will want to register more than one hot key for a single form. To extend the sample code, we will add a second hot key, this time a combination of the Windows key, Alt and Q. The two modifier constants will be combined using the OR operator and a different unique identifier will be used.

To register the second hot key, modify the Load event as follows:

private void Form1_Load(object sender, EventArgs e)
{
    MessageBox.Show(RegisterHotKey(
        (IntPtr)Handle, 1, MOD_WIN, (int)Keys.Q).ToString());
    MessageBox.Show(RegisterHotKey(
        (IntPtr)Handle, 2, MOD_WIN | MOD_ALT, (int)Keys.Q).ToString());
}

As we are registering two hot keys, we should also unregister both combinations. Modify the FormClosing event as follows:

private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
    MessageBox.Show(UnregisterHotKey((IntPtr)Handle, 1).ToString());
    MessageBox.Show(UnregisterHotKey((IntPtr)Handle, 2).ToString());
}

Finally we will modify the overridden WndProc method to change the behaviour according to the hot key pressed. The behaviour of the original hot key will be unchanged. When the additional hot key is used, we will display and activate the form as before and will also display a message box.

To differentiate between the messages for the two hot keys we can read the message's WParam property. When a WM_HOTKEY message is received, the WParam property will contain the unique ID assigned to the hot key during registration. We can therefore check if this property is set to 2 and display a message box only when it is.

Modify the code for the WndProc method as follows and run the program again to test the new behaviour:

protected override void WndProc(ref Message m)
{
    base.WndProc(ref m);

    if (m.Msg == WM_HOTKEY)
    {
        WindowState = FormWindowState.Normal;
        Activate();
        if (m.WParam.ToInt32() == 2) MessageBox.Show("Hot Key!");
    }
}
Link to this Page14 June 2009
TwitterTwitter RSS Feed RSS