Search code examples
windowswinapimfckeyboard-eventsscancodes

How to extract the character from WM_KEYDOWN in PreTranslateMessage(MSG*pMsg)


In a MFC application within PreTranslateMessage(MSG *pMsg) inherited from a CView, I have this:

if (pMsg->message == WM_KEYDOWN) ...

The fields in a WM_KEYDOWN are documented here. The virtual key VK_ value is in pMsg->wParam and pMsg->lParam contains several field, of which bits 16-23 is the keyboard scan code.

So in my code I use:

const int virtualKey = pMsg->wParam;
const int hardwareScanCode = (pMsg->lParam >> 16) & 0x00ff; // bits 16-23

On my non-US keyboard for example, when I press the "#" character, I get the following:

virtualKey == 0xde --> VK_OEM_7 "Used for miscellaneous characters; it can vary by keyboard."
hardwareScanCode == 0x29 (41 decimal)

The character I'd like to "capture" or process differently is ASCII "#", 0x23 (35 decimal).

MY QUESTION

How do I translate the WM_KEYDOWN information to get something I can compare against, regardless of language or keyboard layout? I need to identify the # key whether the user has a standard US keyboard, or something different.

For example, I've been looking at the following functions such as:

MapVirtualKey(virtualkey, MAPVK_VSC_TO_VK);
// previous line is useless, the key VK_OEM_7 doesn't map to anything without the scan code

ToAscii(virtualKey, hardwareScanCode, nullptr, &word, 0);
// previous line returns zero, and zero is written to `word`

Edit:

Long story short: On a U.S. keyboard, SHIFT+3 = #, while on a French keyboard SHIFT+3 = /. So I don't want to look at individual keys, instead I want to know about the character.

When handling WM_KEYDOWN, how do I translate lParam and wParam (the "keys") to find out the character which the keyboard and Windows is about to generate?


Solution

  • I believe this is a better solution. This one was tested with both the standard U.S. keyboard layout and the Canadian-French keyboard layout.

    const int wParam = pMsg->wParam;
    const int lParam = pMsg->lParam;
    const int keyboardScanCode = (lParam >> 16) & 0x00ff;
    const int virtualKey = wParam;
    
    BYTE keyboardState[256];
    GetKeyboardState(keyboardState);
    
    WORD ascii = 0;
    const int len = ToAscii(virtualKey, keyboardScanCode, keyboardState, &ascii, 0);
    if (len == 1 && ascii == '#')
    {
        // ...etc...
    }
    

    Even though the help page seems to hint that keyboardState is optional for the call to ToAscii(), I found that it was required with the character I was trying to detect.