Search code examples
c++winapiwndprocscancodes

Why is MapVirtualKey not returning the extended key flag?


I am trying to synthesize keystrokes via PostMessage() and I need to generate the corresponding lParam.

I am familiar with the KeyStrokeMessage struct.

Here's the code:

///-------------------------------------------------------------------------------------------
/// KeystrokeMessage/KeyLParam struct
///-------------------------------------------------------------------------------------------
typedef struct KeystrokeMessage
{
    unsigned RepeatCount            : 16;
    unsigned ScanCode               : 8;
    unsigned ExtendedFlag           : 1;
    unsigned Reserved               : 4;
    unsigned ContextCode            : 1;
    unsigned PreviousKeyState       : 1;
    unsigned TransitionState        : 1;
} KeyLParam;


///-------------------------------------------------------------------------------------------
/// GetKeyMessageLPARAM:
///     Creates an LPARAM for key messages.
///-------------------------------------------------------------------------------------------
LPARAM GetKeyMessageLPARAM(unsigned aVKey, bool aIsDown, bool aIsSystem)
{
    KeyLParam lp;

    UINT sc = MapVirtualKeyA(aVKey, MAPVK_VK_TO_VSC_EX);

    lp.RepeatCount = 1;
    lp.ScanCode = LOBYTE(sc);
    lp.ExtendedFlag = (HIBYTE(sc) == 0xE0 || HIBYTE(sc) == 0xE1) ? 1 : 0;
    lp.Reserved = 0;
    lp.ContextCode = aIsSystem ? 1 : 0;
    lp.PreviousKeyState = aIsDown ? 0 : 1;
    lp.TransitionState = aIsDown ? 0 : 1;

    return *(LPARAM*)&lp;
}

I also intercepted genuine keystrokes to understand what I'm doing wrong.

Specifically, I'm trying to synthesize an "up arrow" keystroke, which is VK_UP.

A genuine message for the "up arrow" has the wParam set to VK_UP and the lParam has the scancode 0x48 with the extended flag set.

Genuine keystroke for reference:

Picture of the intercepted and interpreted LPARAM

The scancode should be 0xE048 (with the extended flag).

However, MapVirtualKey() returns 72 (0x48) and therefore it is not setting the ExtendedFlag.

The documentation on MSDN states that it would return the extended flag.

I have tried MapVirtualKey() and MapVirtualKeyEx().

In the Ex versions, I've tried both the actual keyboard layout, as well as leaving it as 0.

I am using the correct map type MAPVK_VK_TO_VSC_EX, but I also tried MAPVK_VK_TO_VSC to see if the result is any different.

I even tried going the reverse route, starting with the actual scancode and converting it into a virtual key and then back into a scancode. I get the correct VK_UP but once again trying to get the scancode from that returns the wrong key.

No matter the input, I never get an extended flag returned, even though it's supposed to have one. I also cross-referenced my keyboard layout via https://kbdlayout.info and it is supposed to be 0xE048.

How do I receive the extended flag from MapVirtualKeyA()?


Solution

  • Answering my own question in case anyone else stumbles onto this behaviour:

    The return value is technically correct, as MapVirtualKey defaults to the non-extended version and VK_UP is shared by both the "arrow up" (0xE048) key and Numpad 8 (0x48). Some other keys are also sharing the same Virtual KeyCode, such as VK_RETURN.

    For reference, VK_NUMPAD8 exists and some other layouts may be using it, but not in this case.