Click here to Skip to main content
15,881,709 members
Articles / Programming Languages / C#
Tip/Trick

Setting A Global Hot Key

Rate me:
Please Sign up or sign in to vote.
4.94/5 (10 votes)
24 Jun 2010CPOL5 min read 36.7K   10   8
Goes over how to set, respond to, and remove a global keyboard hook.

Introduction


This article goes over how to set, respond to, and remove a global keyboard hook. I have chosen to keep the associated application VERY simple, so that you may focus on the topic at hand, and adapt it easily to your needs.
In this example, we will be creating an application that is really nothing more than a textbox on a form. When the user presses a key combination, the form will appear (if it’s not already visible). If they press this combination again, the form will be hidden. This will happen whether the application has focus or not, since the hot key is registered globally with the OS.


Background


The operating system needs to keep track of the various hot keys and their associated callback functions. In order to alert the system that you want your thread to receive any matching WM_HOTKEY messages posted to the system, you must register it using the unmanaged RegisterHotKey function.
That call is telling the system how to reach your thread (via a handle), the specific hot key (ID) and what key combination must be pressed in order to invoke it.
The ID is a unique number that the programmer must provide, and is explained in more detail below.
The combination of the handle and ID uniquely identify your hot key. In Windows XP and earlier, if you send an ID and handle that is already in the system, the old value will be clobbered, however, in newer systems, the call will fail, returning zero. There are also system hot keys that cannot be written over (Ctrl-Alt-Delete for instance), and the attempt to register them will fail
When the OS gets the notification that a key has been pressed, it forwards a message to the queue for the registered handle. This message is sent directly to the top of the threads message queue, where it is picked up and handled by WndProc.


Keyboard Driver->System Message Queue -> Thread Message Queue -> WndProc


A hotkey that has been registered will exist until either a call to UnregisterHotKey is made, or the user logs out.


Using the Code


For clarity, I have placed all of the native code and definitions in a static class called "NativeMethods"


To begin, we need to use the unmanaged function RegisterHotKey in order to, well, register the hot key


Here’s the P/invoke signature

C#
DllImport("user32.dll", SetLastError = true)]
internal static extern bool RegisterHotKey(IntPtr hwnd, int id, int fsModifiers, int vk);


hwnd - the handle to your form; the one that’s going to process the desired action.


id - refers to the unique id of this hook. MSDN recommends using the GlobalAddAtom function to obtain this value. We are going to pass in a (hopefully) unique string to be registered in the Global Atom Table. The string is stored in this table, and the function returns a 16 bit integer to use when referencing it. This will help prevent “stomping” on another hot keys ID if you accidentally use a preexisting value.


fsModifiers -the modifier keys that are to pressed in conjunction with your hotkey IE ALT,CTL, SHIFT, etc. This is specified by a set of flags.


vk - refers to the integer value of the key you want to register as the hotkey. We’ll use the managed “Keys” enumeration to set this. (see references below for link to table)



First, we want to get a unique ID for our hotkey by passing in a string to the Global Atom Table. There is a cool way to generate a unique string presented on the P/Invoke site (see references). This uses the thread ID and type.


C#
string atomName = Thread.CurrentThread.ManagedThreadId.ToString("X8") + this.GetType().FullName;

When you add an entry to the Global Atom Table, a unique identifier is returned


C#
short id = NativeMethods.GlobalAddAtom(atomName);

Ok, so now we have a unique value for our hot key’s ID. Now we can call our function to register it:


C#
RegisterHotKey(Handle, HotkeyID, (int)ModifierKeycodes.MOD_ALT, (int)Keys.D);

In the above example, we are using an enumeration for specifying the modifier key (which, for our example is the Alt key).
The values in this enumeration have been tagged as Flags, so they can be OR’d to get different combinations.


With (int)Keys.D We are specifying the "D" key as the primary hot key.


Okey doke, now we have the hot key registered to our application, so how do we respond to the key press?
Remember, we’ve registered our application to be notified when a hot key is pressed. This means that our program will be passed a WM_HOTKEY message into its WndProc loop.
We need to override some of the functionality of this functions function (I couldn’t resist). We’re really only interested in handling this particular message, and specifically our Alt-D hotkey.


C#
protected override void WndProc(ref Message msg)
{
    switch (msg.Msg)
    {
	/** We only care about hotkey messages **/
	case NativeMethods.WM_HOTKEY:
	    /** Is it our hotkey? **/
	    if ((short)msg.WParam == m_HotkeyID)
	    {
		/** if the form is visible, hide it **/
		if (Visible)
		{
		    Hide();
		}
		else
		{
		    /** Otherwise, show it **/
		    Show();
		    BringToFront();
		}
	    }
	    break;
	default:
	    /** pass it back to main WndProc **/
	    base.WndProc(ref msg);
	    break;
    }
}

WndProc is a big ole switch statement that loops to check for messages so it can respond. In our case, we have it set so that if our thread gets a WM_HOTKEY message, we’re going to verify that it’s our combo, and then do something with it…in this case show/hide our form.


Summary and References


In order for your application to be able to respond to a particular combination of keys presses, you must register it as a HotKey with the operating system. When the operating system gets a keypress notification, it check to see if it is registered as a HotKey, and if so, passes a message to the tread that registered it. What the thread does with it is (more or less) up to the programmer, but needs to be intercepted and processed by the overridden WndProc function



  • http://msdn.microsoft.com/en-us/library/ms646309(v=VS.85).aspx
  • http://www.pinvoke.net/default.aspx/user32/registerhotkey.html
  • http://msdn.microsoft.com/en-us/library/ms649060(v=VS.85).aspx
  • http://msdn.microsoft.com/en-us/library/dd375731(v=VS.85).aspx

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionCannot download your solution Pin
rfresh22-Jun-14 8:27
rfresh22-Jun-14 8:27 
GeneralMy vote of 5 Pin
Abhinav S2-Apr-14 7:21
Abhinav S2-Apr-14 7:21 
GeneralMy vote of 5 Pin
Manoj Chamikara17-Aug-13 9:05
Manoj Chamikara17-Aug-13 9:05 
GeneralMy vote of 5 Pin
Mlsoun6-May-13 11:18
Mlsoun6-May-13 11:18 
GeneralMy vote of 5 Pin
Cesar Castano Yepes1-Sep-12 15:30
Cesar Castano Yepes1-Sep-12 15:30 
GeneralDownload solution problem Pin
PBoccagni23-Jun-10 22:25
PBoccagni23-Jun-10 22:25 
GeneralRe: Download solution problem Pin
rigamonk24-Jun-10 8:57
rigamonk24-Jun-10 8:57 
GeneralRe: Download solution problem Pin
PBoccagni24-Jun-10 21:30
PBoccagni24-Jun-10 21:30 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.