Sometimes, your form may need to respond to a hotkey even when it is not focused. There is no built in .NET functionality that allows for this, but it is built into the Win32 API. Today we'll learn to write a library to allow us to use this feature.
First, how do you call a method from the Win32 API? It's not written in .NET, but C/C++. Well, .NET provides a way to call methods defined in non-native libraries: Platform Invocation.
The methods we are interested are defined in the user32.dll. RegisterHotKey and UnregisterHotKey.
I've provided the source in both C# and VB.NET projects, zipped and attached. Download them if you want to follow along.
Let's start by creating a new Class Library called Hotkeys. In this library, create a public class called GlobalHotkey. Add a reference to System.Windows.Forms, and add the following using statements at the top:
using System.Windows.Forms; using System.Runtime.InteropServices;
Now define the important methods: the external hotkey methods:
[DllImport("user32.dll")]
private static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vk);
[DllImport("user32.dll")]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
Note that you're not providing a method body. The method is defined in user32.dll, you're just adding a way for your application to directly call that method. FYI, hWnd refers to the window handle (we will pass our form's handle), id is the unique identifier of the hotkey, fsModifiers is the integer representation of the modifier keys (shift/alt/ctrl/win) that you want pressed with your key, and vk is the virtual key code for the hotkey.
Now, we can start building the class around these methods. First, lets add the fields we want in the class:
private int modifier; private int key; private IntPtr hWnd; private int id;
Now the constructor:
public GlobalHotkey(int modifier, Keys key, Form form)
{
this.modifier = modifier;
this.key = (int)key;
this.hWnd = form.Handle;
id = this.GetHashCode();
}
In the constructor, we set all our fields to their proper values. Note that we are requiring that the caller pass in a reference to a System.Windows.Form. That is so we can grab the handle from that form. Also, we are calling GetHashCode to create the unique id. But we must override this method to make it useful to us:
public override int GetHashCode()
{
return modifier ^ key ^ hWnd.ToInt32();
}
What we're doing here is combining the fields via XOR as suggested by the MSDN.
Now we create the two public methods. We want the user to be able to register and unregister these hotkeys whenever they want, so that's why we make them public methods:
public bool Register()
{
return RegisterHotKey(hWnd, id, modifier, key);
}
public bool Unregiser()
{
return UnregisterHotKey(hWnd, id);
}
And that's basically it. Your library is ready to use as is. Your code file should look like this. But for simplicity's sake, let's add a Constants class to make it easier to use:
namespace Hotkeys
{
public static class Constants
{
//modifiers
public const int NOMOD = 0x0000;
public const int ALT = 0x0001;
public const int CTRL = 0x0002;
public const int SHIFT = 0x0004;
public const int WIN = 0x0008;
//windows message id for hotkey
public const int WM_HOTKEY_MSG_ID = 0x0312;
}
}
This file defines some useful values that we will need to actually define and use hotkeys.
=================USAGE============================
Now, let's use the library we just created in a Windows Forms application. Add a new project to your solution, and call it HotkeysWin. Add a reference to your library in the forms project. You should be able to see your library in the "Projects" tab of the Add Reference dialog.
Add handlers for your form's Load and FormClosing events. You can do this by clicking the lightning bold in the properties window, and double-clicking the appropriate entries.
Add a multiline TextBox to your form.
Now for the codebehind:
using System;
using System.Windows.Forms;
using Hotkeys;
namespace HotkeyWin
{
public partial class Form1 : Form
{
private Hotkeys.GlobalHotkey ghk;
public Form1()
{
InitializeComponent();
ghk = new Hotkeys.GlobalHotkey(Constants.ALT + Constants.SHIFT, Keys.O, this);
}
private void HandleHotkey()
{
WriteLine("Hotkey pressed!");
}
protected override void WndProc(ref Message m)
{
if (m.Msg == Hotkeys.Constants.WM_HOTKEY_MSG_ID)
HandleHotkey();
base.WndProc(ref m);
}
private void Form1_Load(object sender, EventArgs e)
{
WriteLine("Trying to register SHIFT+ALT+O");
if (ghk.Register())
WriteLine("Hotkey registered.");
else
WriteLine("Hotkey failed to register");
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (!ghk.Unregiser())
MessageBox.Show("Hotkey failed to unregister!");
}
private void WriteLine(string text)
{
textBox1.Text += text + Environment.NewLine;
}
}
}
Notice in the constructor how we create a new hotkey:
ghk = new Hotkeys.GlobalHotkey(Constants.ALT + Constants.SHIFT, Keys.O, this);
You add modifiers to your hotkey by adding their values. So if you wanted to declare one that used CTRL+ALT+F, you would use it like this:
ghk = new Hotkeys.GlobalHotkey(Constants.CTRL + Constants.ALT, Keys.F, this);
To declare one with only one modifier, just use the value without adding it to anything. To use no modifier key, do this:
ghk = new Hotkeys.GlobalHotkey(Constants.NOMOD, Keys.O, this);
But be careful when doing this. If you bind your form to a hotkey that is just a letter key, it may interfere with the operation of other applications.
Also notice that we override the WndProc method. This is the method that is called to handle all windows messages. We've defined the ID of the message we're looking for in Hotkeys.Constants.WM_HOTKEY_MSG_ID. So we specifically want to take action when that message comes through.
I hope this has been enlightening. Look out for the VB.NET version, coming soon. I'll add a link when it is up.
Attached File(s)
-
Hotkeys.zip (66.35K)
Number of downloads: 3225





MultiQuote






|