Creating and Responding to System-Wide Hotkeys


Win32 allows you to set up combinations of key-presses which can be associated with an application on a system-wide scope, called Hotkeys. When a user presses a registered HotKey it doesn't matter which application is active when they pressed it, the hot-key message will get routed to your application.

About HotKeys

There are two types of HotKeys:

  1. Application activation HotKeys
    These are the simplest type. When the HotKey is selected your application is activated. They are very simple to set up but they only activate your application - you can't determine if this was caused by the HotKey being pressed rather than some other event, nor decide on any specific action to take.
  2. Registered HotKeys
    This type of HotKey sends a WM_HOTKEY message to your application when the HotKey is pressed, so you can choose how to respond to the key. Setting up this type is slightly more complex and you have to subclass your application's form to intercept the HotKey messages.

This article describes how to create and respond to the second type, Registered HotKeys. If you want to create a simple activation HotKey, this information is described in the HotKey Control sample. This also provides an implementation of the Common Controls HotKey control which you can use in an application to make it easier for a user to set-up HotKeys.

Registering a HotKey

Registering a system-wide HotKey is achieved in two steps:

  1. Creating a globally unique identifier for the HotKey. You do this using the Win32 Global Atom database.
  2. Registering the HotKey itself.

1. The Win32 Atom Databases

Win32 Atom databases store a list of unique strings which are internally identified by an 16 bit integer ID. The Global Atom database creates string lists which are accessible to all processes. In addition to this each process on the system can have its own Local Atom database. These functions could be quite useful for other VB purposes - if you need to store a list of unique strings and IDs in an application, you could take advantage of the Atom database to do it. However, as there is only one atom database per process you do have to be careful! Note that an applications process includes both the executable and any OCXs or DLLs being used in that executable.

The Global Atom database functions in Win32 are GlobalAddAtom, GlobalDeleteAtom, GlobalFindAtom and GlobalGetAtomName. The first step of registering a HotKey is to create a string that is unlikely to be in use by any other application on the system and then get an ID for it by calling GlobalAddAtom for that string. In the class supplied with the demonstration project the string is created by specifying a local key string to which the executable name and the current GetTickCount value are appended.

2. Registering the HotKey

Registering the key itself is achieved using Win32's unusually sensibly-named RegisterHotKey function. This takes the window handle you want to send the messages to, the globally unique identifier you created using the Global Atom database, a bit flag containing the Shift, Ctrl, Alt or Windows Key keys which have to be pressed for the HotKey and the Virtual Key Code of the key you want to register. Note that in VB the VBRUN library enumeration KeyCodeConstants corresponds directly to the Win32 Virtual Key Code constants. These are the key values that are passed to you in the KeyUp and KeyDown events.

Responding to HotKeys

Once you've set up a HotKey, you can respond to it by subclassing the WM_HOTKEY message sent to the window. The wParam of the WM_HOTKEY message contains the globally unique identifier you assigned to the HotKey, and the lParam contains the virtual key code in the HiWord and the key modifiers in the LoWord.

Tip: Extract HiWords and LoWords from a Long value

This is easy to do using the And operator and emulating a Right Shift by dividing:

  LoWord = (Value And &HFFFF&) 
  HiWord = (Value \ &H10000) And &HFFFF&

There is a problem with this techique you have to be aware of if the HiWord of the Value has its highest bit set. In VB, there is no way of defining a Long variable as "Unsigned", so the high bit of a Long is used to identify whether the number is positive or negative. If the Value has the high bit set, then the result of the HiWord comparison above will be wrong (for example, if Value was &HFFFFFFFF, which VB interprets as -1, then the HiWord method above will yield 0!). To work around this problem, rewrite HiWord like this:

  ' Check for high bit (sign flag):
  If (Value And &H80000000) = &H80000000 Then
    ' Strip it out whilst shifting then add it back in again afterwards:
    HiWord = (((Value And &H7FFFFFFF) \ &H10000) Or &H8000) _
       And &HFFFF&
    ' Standard shift method:
    HiWord = (Value \ &H10000) And &HFFFF&
  End If

In Use

The cRegHotKey class supplied in the download wraps up all of this functionality into a simple class which you can drop into a project. Using the class you do this:

  1. Declare a WithEvents instance of the class in your project:
       Private WithEvents m_cHK As cRegHotKey
  2. Create an instance of the class and tell it your form's window handle:
       Set m_cHK = New cRegHotKey>
       m_cHK.Attach Me.hwnd
  3. Add a HotKey (or as many as you like!):
       ' This registers CTRL-ALT-C as "MyHotKey" 
       m_cHK.RegisterKey "MyHotKey", vbKeyC, MOD_CONTROL Or MOD_ALT
  4. Respond to the keypress throught HotKeyPress event
       Private Sub m_cHK_HotKeyPress(ByVal sName As String, _
          ByVal eModifiers As EHKModifiers, _
          ByVal eKey As KeyCodeConstants _
          If (sName = "MyHotKey") Then
                ' Do your processing here!
          End If
       End Sub

The alternative download implements an entirely pointless use of the class: CTRL-ALT-C starts and stops a small earthquake on your system. CTRL-ALT-X used to attempt to kill any instances of the supremely smug Microsoft Assistant thingy it finds on the screen but only works on old versions of Office. More usefully, it also demonstrates using the HotKey control to set up the keys on the screen (this control is included compiled into the source code).