Add Hotkey Support
Tell your application to respond to system-wide hotkeys.
by Karl E. Peterson
Technology Toolbox: VB6, VB5
Q: Add Hotkey Support
I want to create a program that usually runs in the background, but pops up and becomes the active task if the user presses (for example) Ctrl-1 at any time. Someone pointed me to the RegisterHotKey API. Can you tell me more about it? Is a subclass required in this case?
My preference when subclassing is to have the messages dispatched from the generic BAS module sink (by necessity, due to AddressOf limitations) to a companion class that in turn notifies its associated form as needed. The details of this operation could fill an entire column, but luckily, various implementations abound on the Internet, and you can download my solution here. I've wrapped this specific task up in a CHotKey class, which you set up like this:
' Member variables Private WithEvents m_Hotkey As CHotKey Private Sub Form_Load() ' Setup instance of hotkey tracking ' class, using Ctrl-1 as the trigger. Set m_Hotkey = New CHotKey m_Hotkey.hWnd = Me.hWnd m_Hotkey.SetHotKey vbKey1, _ vbCtrlMask End Sub
Each time the class sinks a WM_HOTKEY message, it notifies the form by raising a Hotkey event, and the form then brings itself front and center:
Private Sub m_Hotkey_Hotkey() ' Reposition, and bring forward. With Me .WindowState = vbNormal .Move (Screen.Width - .Width) \ _ 2,(Screen.Height - .Height) _ \ 2 .Show End With End Sub
You could've wrapped these response details into the companion class just as easily, but they're as likely as not to change from application to application, so I felt they were better left within the individual form.
RegisterHotKey requires four parameters. The first parameter tells the system which window to notify when the user presses a hotkey. The second parameter is an identification value unique to your application. The third and fourth parameters identify the modifier key(s) and virtual key codes for your desired hotkey. The ID value and the modifier key value(s) are slightly counterintuitive in some aspects.
You can generate a system-wide unique value to use as your hotkey's ID value by calling the GlobalAddAtom API and passing a string representation of the current time. GlobalAddAtom returns a value in the range &hC000-&hFFFF, which is the documented range shared DLLs (as opposed to standalone EXEs) should use with RegisterHotKey. Fortunately, no ill effect seems to result from using this convenient number-generation scheme. Be sure to match a call to GlobalDeleteAtom for each call to GlobalAddAtom to avoid overfilling the global atom table.
Another potential problem exists in that the system-defined key modifiers—MOD_ALT, MOD_CONTROL, and MOD_SHIFT—have values that don't match their VB counterparts (ShiftConstants) directly. The Shift and Alt modifier values are flip-flopped in these two constant schemes, so you need to recombine the modifiers for the API if you want to offer the class consumer IntelliSense support for your Modifiers parameter (see Listing 1).
Hotkeys work on a first-come, first-served basis. If another application has registered your desired hotkey already, RegisterHotKey fails and Err.LastDllError contains 1409—the error code for ERROR_HOTKEY_ALREADY_REGISTERED. Your only option should that occur is to fall back to second, or even third, choices and alert your user. On NT-class systems, don't use F12 for your hotkey, because NT reserves this particular key for use by debuggers. —K.E.P.
About the Author