What's the correct way to convert a message from SetWindowsHookEx
with WH_KEYBOARD_LL
into a useful representation of the key pressed?
I'm aware this will most likely involve understanding the mappings of the local keyboard. (NB: I'm only considering when a key is pressed, not when it's released for simplicity)
Broadly speaking there seem to be three scenarios:
- Special key pressed (Ctrl/Escape/Shift/Alt)
- Standard key pressed (
A-Z
,0-9
, etc... Note thata
andA
both read asA
) - Some hard to define cases like number pad and F1-F12
Special keys can be dealt with as required and there are some useful lookups in System.Windows.Forms.Keys
but if I were to do (on a UK-English Keyboard) an exclamation mark, it's detected as Shift-Down1Shift up
Since I'm hooking at too low a level (I believe) to get the codes after they've been through the keyboard "conversion" layer, I'm curious how I'd go about correctly interpreting them.
As to why I'm doing this. It started off as a way to give convenient shortcuts to a media player I've written that will work anywhere, even inside games (some games seem to intercept key strokes and prevent them propagating to the OS). I've actually got enough for what I need, as long as it's me using the app (only likely user) but my curiosity is piqued as to how I could take this further if I wanted to.
Depending on the language (and its use of dead keys), this can be extremely complicated.
ToAsciiEx
works for the simple situations, but if there are dead keys involved, or an IME, things get very complicated very quickly.Michael Kaplan's blog has an extensive series of articles talking about keyboard layouts and the Keyboard Layout Creator, which is a tool for creating keyboard layouts (the simplest way of mapping keys to characters). The Keyboard Layout Creator is used when a single set of keystrokes maps to a single character. In these cases, a mapping can be done, but it becomes somewhat tricky to do when more than one key is needed to determine a character.
Languages such as Japanese, Chinese, or Korean do not have a single mapping from keystrokes to characters. For these languages, an Input Method Editor is needed; these typically use Text Services Framework, which is an extensive set of APIs that input method editors (and other text services, such as handwriting recognition and speech recognition) use to communicate with the application to determine a mapping from keystrokes to characters. Because the mapping from keystrokes to characters with an IME is defined by code, the low-level keyboard hook often cannot perform this mapping at all.
Eric's absolutely correct that there is not a simple way to do this. I'm attaching my best-effort code below which gets me 95% of the way there (Using
ToUnicodeEx
).It doesn't catch special (IME/Dead key) characters correctly (AltGr-4 for a
€
on a UK-English Keyboard). It does, however, preserve the state of the kernel-mode keyboard buffer (so it doesn't interfere with advanced key combos being typed).Note that this code is not production-ready and needs proper error handling, etc, etc. It's also only tested on a few keyboard layouts. YMMV.