4 Login   4 Register Account    

 
 

 
 

 
 
 

 
 
 
 

 
 




Activate your application with global hotkeys

Use RegisterHotKey Windows API function and subclassing to detect when a hotkey is pressed even if your application doesn't have the focus.


 Printer friendly version

Some Win32 applications may need to be activated quickly and easily by means of a global hotkey, such as Ctrl+F6 or Windows+Q. Doing this with a Windows Form application isn't easily, though.

There are at least two ways to intercept hotkeys in Windows: by setting up a system-level keyboard hook or by registering a global hotkey. Implementing a system-level keyboard hook isn't for the faint of heart and may require some C++ wizardry; fortunately, you rarely need to install a keyboard hook, unless you want to monitor all keys being pressed by the end users while they are working with other applications.

This leaves us with global hotkeys. Global hotkeys are remarkably simpler than system-level keyboard hooks, even though they still need some labour for a correct implementation. First of all, we need to access some functions in the Windows API:

' Windows API functions and constants
Private Declare Function RegisterHotKey Lib "user32" (ByVal hwnd As IntPtr, ByVal id As Integer, _
   ByVal fsModifiers As Integer, ByVal vk As Integer) As Integer
Private Declare Function UnregisterHotKey Lib "user32" (ByVal hwnd As IntPtr, ByVal id As Integer) _
   As Integer
Private Declare Function GlobalAddAtom Lib "kernel32" Alias "GlobalAddAtomA" (ByVal lpString As _
   String) As Short
Private Declare Function GlobalDeleteAtom Lib "kernel32" (ByVal nAtom As Short) As Short

Private Const MOD_ALT As Integer = 1
Private Const MOD_CONTROL As Integer = 2
Private Const MOD_SHIFT As Integer = 4
Private Const MOD_WIN As Integer = 8

// this code assumes the following using statement
//     using System.Runtime.InteropServices;

// Windows API functions and constants
[DllImport("user32", SetLastError=true)]
private static extern int RegisterHotKey (IntPtr hwnd, int id, int fsModifiers, int vk);
[DllImport("user32", SetLastError=true)]
private static extern int UnregisterHotKey (IntPtr hwnd, int id);
[DllImport("kernel32", SetLastError=true)]
private static extern short GlobalAddAtom (string lpString);
[DllImport("kernel32", SetLastError=true)]
private static extern short GlobalDeleteAtom (short nAtom);

private const int MOD_ALT = 1;
private const int MOD_CONTROL = 2;
private const int MOD_SHIFT = 4;
private const int MOD_WIN = 8;

Next, you need to install the hotkey, which you do by calling the RegisterHotKey API function: this function takes a unique id, and MSDN docs explain that you should call GlobalAddAtom to get such an id. Besides, you must store this id in a form variable, because you need it later, to unregister the class and delete the global atom you created:

' the id for the hotkey
Dim hotkeyID As Short

' register a global hot key
Sub RegisterGlobalHotKey(ByVal hotkey As Keys, ByVal modifiers As Integer)
   Try
      ' use the GlobalAddAtom API to get a unique ID (as suggested by MSDN docs)
      Dim atomName As String = AppDomain.GetCurrentThreadId.ToString("X8") & Me.Name
      hotkeyID = GlobalAddAtom(atomName)
      If hotkeyID = 0 Then
         Throw New Exception("Unable to generate unique hotkey ID. Error code: " & _
            Marshal.GetLastWin32Error().ToString)
      End If

      ' register the hotkey, throw if any error
      If RegisterHotKey(Me.Handle, hotkeyID, modifiers, CInt(hotkey)) = 0 Then
         Throw New Exception("Unable to register hotkey. Error code: " & _
            Marshal.GetLastWin32Error.ToString)
      End If
   Catch ex As Exception
      ' clean up if hotkey registration failed
      UnregisterGlobalHotKey()
   End Try
End Sub

' unregister a global hotkey
Sub UnregisterGlobalHotKey()
   If Me.hotkeyID <> 0 Then
      UnregisterHotKey(Me.Handle, hotkeyID)
      ' clean up the atom list
      GlobalDeleteAtom(hotkeyID)
      hotkeyID = 0
   End If
End Sub

// the id for the hotkey
short hotkeyID;

// register a global hot key
void RegisterGlobalHotKey( Keys hotkey,  int modifiers)
{
   try
   {
      // use the GlobalAddAtom API to get a unique ID (as suggested by MSDN docs)
      string atomName = AppDomain.GetCurrentThreadId().ToString("X8") + this.Name;
      hotkeyID = GlobalAddAtom(atomName);
      if ( hotkeyID == 0 )
      {
         throw new Exception("Unable to generate unique hotkey ID. Error code: " +
            Marshal.GetLastWin32Error().ToString());
      }

      // register the hotkey, throw if any error
      if ( RegisterHotKey(this.Handle, hotkeyID, modifiers, (int) hotkey) == 0 )
      {
         throw new Exception("Unable to register hotkey. Error code: " + Marshal.GetLastWin32Error()
            .ToString());
      }
   }
   catch ( Exception e )
   {
      // clean up if hotkey registration failed
      UnregisterGlobalHotKey();
   }
}

// unregister a global hotkey
void UnregisterGlobalHotKey()
{
   if ( this.hotkeyID != 0 )
   {
      UnregisterHotKey(this.Handle, hotkeyID);
      // clean up the atom list
      GlobalDeleteAtom(hotkeyID);
      hotkeyID = 0;
   }
}

You typically call the RegisterGlobalHotKey method from the form's Load event handler and the UnregisterGlobalHotKey method from the form's Closed event handler. It is very important that you don't skip the unregistration step, else you will leak resources. The first argument you pass to RegisterGlobalHotKey is one of the System.Windows.Forms.Keys constants, whereas the second argument is either the MOD_WIN value (the Windows logo key) or a combination of MOD_SHIFT, MOD_CONTROL, and MOD_ALT integer constants.

Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Load
   ' register the Shift+Ctrl+F6 hot key
   RegisterGlobalHotKey(Keys.F6, MOD_SHIFT Or MOD_CONTROL)
End Sub

Private Sub Form1_Closed(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Closed
   ' unregister the hotkey (NEVER FORGET THIS!)
   UnregisterGlobalHotKey()
End Sub

private void Form1_Load(object sender, EventArgs e)
{
   // register the Shift+Ctrl+F6 hot key
   RegisterGlobalHotKey(Keys.F6, MOD_SHIFT | MOD_CONTROL);
}

private void Form1_Closed(object sender, System.EventArgs e)
{
   // unregister the hotkey (NEVER FORGET THIS!)
   UnregisterGlobalHotKey();
}

After registering the hotkey, Windows sends your form an WM_HOTKEY message whenever the end user presses the key combination. To trap this message you must override the WndProc protected method and take appropriate action (usually activate the form):

Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
   ' let the base class process the message
   MyBase.WndProc(m)

   ' if this is a WM_HOTKEY message, notify the parent object
   Const WM_HOTKEY As Integer = &H312
   If m.Msg = WM_HOTKEY Then
      ' do whatever you wish to do when the hotkey is pressed
      ' in this example we activate the form and display a messagebox
      Me.Activate()
      MessageBox.Show("Hotkey has been pressed")
   End If
End Sub

protected override void WndProc(ref System.Windows.Forms.Message m)
{
   // let the base class process the message
   base.WndProc(ref m);

   // if this is a WM_HOTKEY message, notify the parent object
   const int WM_HOTKEY = 0x312;
   if ( m.Msg == WM_HOTKEY )
   {
      // do whatever you wish to do when the hotkey is pressed
      // in this example we activate the form and display a messagebox
      this.Activate();
      MessageBox.Show("Hotkey has been pressed");
   }
}

 

It takes less than one minute to subscribe to our newsletter. You will receive additional material right in your mailbox. Best of all, it’s free!