Windows virtual key codes

10,287

Solution 1

There is no full answer. Thank you everybody for helping. After more research I wrote complete function that converts virtualKey to std::string description.

* std::basic_string < TCHAR > Version: *

typedef std::basic_string<TCHAR> tstring;

tstring VirtualKeyCodeToString(UCHAR virtualKey)
{
    UINT scanCode = MapVirtualKey(virtualKey, MAPVK_VK_TO_VSC);

    TCHAR szName[128];
    int result = 0;
    switch (virtualKey)
    {
        case VK_LEFT: case VK_UP: case VK_RIGHT: case VK_DOWN:
        case VK_RCONTROL: case VK_RMENU:
        case VK_LWIN: case VK_RWIN: case VK_APPS:
        case VK_PRIOR: case VK_NEXT:
        case VK_END: case VK_HOME:
        case VK_INSERT: case VK_DELETE:
        case VK_DIVIDE:
        case VK_NUMLOCK:
            scanCode |= KF_EXTENDED;
        default:
            result = GetKeyNameText(scanCode << 16, szName, 128);
    }
    if(result == 0)
        throw std::system_error(std::error_code(GetLastError(), std::system_category()),
                                "WinAPI Error occured.");
    return szName;
}

std::string version:

std::string VirtualKeyCodeToString(UCHAR virtualKey)
{
    UINT scanCode = MapVirtualKey(virtualKey, MAPVK_VK_TO_VSC);

    CHAR szName[128];
    int result = 0;
    switch (virtualKey)
    {
        case VK_LEFT: case VK_UP: case VK_RIGHT: case VK_DOWN:
        case VK_RCONTROL: case VK_RMENU:
        case VK_LWIN: case VK_RWIN: case VK_APPS:
        case VK_PRIOR: case VK_NEXT:
        case VK_END: case VK_HOME:
        case VK_INSERT: case VK_DELETE:
        case VK_DIVIDE:
        case VK_NUMLOCK:
            scanCode |= KF_EXTENDED;
        default:
            result = GetKeyNameTextA(scanCode << 16, szName, 128);
    }
    if(result == 0)
        throw std::system_error(std::error_code(GetLastError(), std::system_category()),
                                "WinAPI Error occured.");
    return szName;
}

Solution 2

A simple way to convert the VK code to a text representation of the key is to:

  1. Use MapVirtualKey to convert the VK code to a scan code.
  2. Do a bit shift to convert that value into a long where bits 16-23 are the scan code
  3. Use GetKeyNameText to obtain the name of the key.

For example:

WCHAR name[1024];
UINT scanCode = MapVirtualKeyW(VK_CAPITAL, MAPVK_VK_TO_VSC);
LONG lParamValue = (scanCode << 16);
int result = GetKeyNameTextW(lParamValue, name, 1024);
if (result > 0)
{
    std::wcout << name << endl; // Output: Caps Lock
}

If you're doing this in response to a WM_KEYDOWN or other message that passes the scan code in the LPARAM you can skip the first two steps, since those are just there to massage the VK code into a properly formatted input for GetKeyNameText. For more information about the function and the format of the first parameter to GetKeyNameText see the documentation at MSDN

Note: I used the W variant on the API calls, so you'd actually need to use a std::wstring to pass the key name, but you can change it easily to use the A version. Also, if you need to pass a keyboard layout to get the proper scan code, you can use MapVirtualKeyEx.

Share:
10,287
Inline
Author by

Inline

Updated on June 05, 2022

Comments

  • Inline
    Inline almost 2 years

    How can I implement a function like std::string VirtualKeyCodeToStdString(UCHAR key) which returns virtual keys descriptions?

    Example: input is VK_CAPITAL, return value is std::string("Caps Lock")

  • IInspectable
    IInspectable almost 8 years
    Your std::string version needs to call GetKeyNameTextA.
  • Inline
    Inline almost 8 years
    @IInspectable it depends on settings. But you true.
  • IInspectable
    IInspectable almost 8 years
    Those external settings are outside your control. That's why you need to be explicit about the function version, when being explicit about the character type.
  • Zinovy Nis
    Zinovy Nis over 6 years
    It's more clear to use KF_EXTENDED instead of hard-coded 0x100.