decasteljau
decasteljau

Reputation: 8033

Force GetKeyNameText to english

The Win32 function GetKeyNameText will provide the name of keyboard keys in the current input locale.

From MSDN:

The key name is translated according to the layout of the currently installed keyboard, thus the function may give different results for different input locales.

Is it possible to force the input locale for a short amount of time? Or is there another alternative to GetKeyNameText that will always return the name in English?

Upvotes: 5

Views: 1448

Answers (2)

DJm00n
DJm00n

Reputation: 1431

WARNING: GetKeyNameText is broken (it returns wrong A-Z key names for non-english keyboard layouts since it uses MapVirtualKey with MAPVK_VK_TO_CHAR that is broken). I cannot recommend dealing with this method at all. :)

You can use this table to map scan code to HID Usage codes and HID Usage Names:

Scan code HID Usage HID Usage Name
0x00FF 0x0007001 ErrorRollOver
0x001E 0x0007004 Keyboard A
0x0030 0x0007005 Keyboard B
0x002E 0x0007006 Keyboard C
0x0020 0x0007007 Keyboard D
0x0012 0x0007008 Keyboard E
0x0021 0x0007009 Keyboard F
0x0022 0x000700A Keyboard G
0x0023 0x000700B Keyboard H
0x0017 0x000700C Keyboard I
0x0024 0x000700D Keyboard J
0x0025 0x000700E Keyboard K
0x0026 0x000700F Keyboard L
0x0032 0x0007010 Keyboard M
0x0031 0x0007011 Keyboard N
0x0018 0x0007012 Keyboard O
0x0019 0x0007013 Keyboard P
0x0010 0x0007014 Keyboard Q
0x0013 0x0007015 Keyboard R
0x001F 0x0007016 Keyboard S
0x0014 0x0007017 Keyboard T
0x0016 0x0007018 Keyboard U
0x002F 0x0007019 Keyboard V
0x0011 0x000701A Keyboard W
0x002D 0x000701B Keyboard X
0x0015 0x000701C Keyboard Y
0x002C 0x000701D Keyboard Z
0x0002 0x000701E Keyboard 1 and Bang
0x0003 0x000701F Keyboard 2 and At
0x0004 0x0007020 Keyboard 3 And Hash
0x0005 0x0007021 Keyboard 4 and Dollar
0x0006 0x0007022 Keyboard 5 and Percent
0x0007 0x0007023 Keyboard 6 and Caret
0x0008 0x0007024 Keyboard 7 and Ampersand
0x0009 0x0007025 Keyboard 8 and Star
0x000A 0x0007026 Keyboard 9 and Left Bracket
0x000B 0x0007027 Keyboard 0 and Right Bracket
0x001C 0x0007028 Keyboard Return Enter
0x0001 0x0007029 Keyboard Escape
0x000E 0x000702A Keyboard Delete
0x000F 0x000702B Keyboard Tab
0x0039 0x000702C Keyboard Spacebar
0x000C 0x000702D Keyboard Dash and Underscore
0x000D 0x000702E Keyboard Equals and Plus
0x001A 0x000702F Keyboard Left Brace
0x001B 0x0007030 Keyboard Right Brace
0x002B 0x0007031 Keyboard Pipe and Slash
0x002B 0x0007032 Keyboard Non-US
0x0027 0x0007033 Keyboard SemiColon and Colon
0x0028 0x0007034 Keyboard Left Apos and Double
0x0029 0x0007035 Keyboard Grave Accent and Tilde
0x0033 0x0007036 Keyboard Comma
0x0034 0x0007037 Keyboard Period
0x0035 0x0007038 Keyboard QuestionMark
0x003A 0x0007039 Keyboard Caps Lock
0x003B 0x000703A Keyboard F1
0x003C 0x000703B Keyboard F2
0x003D 0x000703C Keyboard F3
0x003E 0x000703D Keyboard F4
0x003F 0x000703E Keyboard F5
0x0040 0x000703F Keyboard F6
0x0041 0x0007040 Keyboard F7
0x0042 0x0007041 Keyboard F8
0x0043 0x0007042 Keyboard F9
0x0044 0x0007043 Keyboard F10
0x0057 0x0007044 Keyboard F11
0x0058 0x0007045 Keyboard F12
0xE037
0x54
0x0007046 Keyboard PrintScreen
0x0046 0x0007047 Keyboard Scroll Lock
0xE11D45
0xE046
0x45
0x0007048 Keyboard Pause
0xE052 0x0007049 Keyboard Insert
0xE047 0x000704A Keyboard Home
0xE049 0x000704B Keyboard PageUp
0xE053 0x000704C Keyboard Delete Forward
0xE04F 0x000704D Keyboard End
0xE051 0x000704E Keyboard PageDown
0xE04D 0x000704F Keyboard RightArrow
0xE04B 0x0007050 Keyboard LeftArrow
0xE050 0x0007051 Keyboard DownArrow
0xE048 0x0007052 Keyboard UpArrow
0x0045
0xE045
0x0007053 Keypad Num Lock and Clear
0xE035 0x0007054 Keypad Forward Slash
0x0037 0x0007055 Keypad Star
0x004A 0x0007056 Keypad Dash
0x004E 0x0007057 Keypad Plus
0xE01C 0x0007058 Keypad ENTER
0x004F 0x0007059 Keypad 1 and End
0x0050 0x000705A Keypad 2 and Down Arrow
0x0051 0x000705B Keypad 3 and PageDn
0x004B 0x000705C Keypad 4 and Left Arrow
0x004C 0x000705D Keypad 5
0x004D 0x000705E Keypad 6 and Right Arrow
0x0047 0x000705F Keypad 7 and Home
0x0048 0x0007060 Keypad 8 and Up Arrow
0x0049 0x0007061 Keypad 9 and PageUp
0x0052 0x0007062 Keypad 0 and Insert
0x0053 0x0007063 Keypad Period
0x0056 0x0007064 Keyboard Non-US Slash Bar
0xE05D 0x0007065 Keyboard Application
0xE05E 0x0007066 Keyboard Power
0x0059 0x0007067 Keypad Equals
0x0064 0x0007068 Keyboard F13
0x0065 0x0007069 Keyboard F14
0x0066 0x000706A Keyboard F15
0x0067 0x000706B Keyboard F16
0x0068 0x000706C Keyboard F17
0x0069 0x000706D Keyboard F18
0x006A 0x000706E Keyboard F19
0x006B 0x000706F Keyboard F20
0x006C 0x0007070 Keyboard F21
0x006D 0x0007071 Keyboard F22
0x006E 0x0007072 Keyboard F23
0x0076 0x0007073 Keyboard F24
0x007E 0x0007085 Keypad Comma
0x0073 0x0007087 Keyboard International1
0x0070 0x0007088 Keyboard International2
0x007D 0x0007089 Keyboard International3
0x0079 0x000708A Keyboard International4
0x007B 0x000708B Keyboard International5
0x005C 0x000708C Keyboard International6
0x0072 0x0007090 Keyboard LANG1
0x0071 0x0007091 Keyboard LANG2
0x0078 0x0007092 Keyboard LANG3
0x0077 0x0007093 Keyboard LANG4
0x0076 0x0007094 Keyboard LANG5
0x001D 0x00070E0 Keyboard LeftControl
0x002A 0x00070E1 Keyboard LeftShift
0x0038 0x00070E2 Keyboard LeftAlt
0xE05B 0x00070E3 Keyboard Left GUI
0xE01D 0x00070E4 Keyboard RightControl
0x0036 0x00070E5 Keyboard RightShift
0xE038 0x00070E6 Keyboard RightAlt
0xE05C 0x00070E7 Keyboard Right GUI

OR if you're really-really want to get this info from keyboard layout - then you can load and parse it manually from layout dll file (kbdus.dll, kbdger.dll etc).

There is a bunch of undocumented stuff involved:

  • In order to get proper keyboard layout dll file name first you need to get KLID string (keyboard layout id, see here for the list). US English is 00000409. If you have HKL (from GetKeyboardLayout or WM_INPUTLANGCHANGE) then you can convert it to KLID string with such code:
// Returns KLID string of size KL_NAMELENGTH
// Same as GetKeyboardLayoutName but for any HKL
// https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/windows-language-pack-default-values
BOOL GetKLIDFromHKL(HKL hkl, _Out_writes_(KL_NAMELENGTH) LPWSTR pwszKLID)
{
    bool succeded = false;

    if ((HIWORD(hkl) & 0xf000) == 0xf000) // deviceId contains layoutId
    {
        WORD layoutId = HIWORD(hkl) & 0x0fff;

        HKEY key;
        CHECK_EQ(::RegOpenKeyW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts", &key), ERROR_SUCCESS);

        DWORD index = 0;
        while (::RegEnumKeyW(key, index, pwszKLID, KL_NAMELENGTH) == ERROR_SUCCESS)
        {
            WCHAR layoutIdBuffer[MAX_PATH] = {};
            DWORD layoutIdBufferSize = sizeof(layoutIdBuffer);
            if (::RegGetValueW(key, pwszKLID, L"Layout Id", RRF_RT_REG_SZ, nullptr, layoutIdBuffer, &layoutIdBufferSize) == ERROR_SUCCESS)
            {
                if (layoutId == std::stoul(layoutIdBuffer, nullptr, 16))
                {
                    succeded = true;
                    DBGPRINT("Found KLID 0x%ls by layoutId=0x%04x", pwszKLID, layoutId);
                    break;
                }
            }
            ++index;
        }
        CHECK_EQ(::RegCloseKey(key), ERROR_SUCCESS);
    }
    else
    {
        WORD langId = LOWORD(hkl);

        // deviceId overrides langId if set
        if (HIWORD(hkl) != 0)
            langId = HIWORD(hkl);

        std::swprintf(pwszKLID, KL_NAMELENGTH, L"%08X", langId);
        succeded = true;

        DBGPRINT("Found KLID 0x%ls by langId=0x%04x", pwszKLID, langId);
    }

    return succeded;
}
  • Then with KLID string you need to go to HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layouts\%KLID% registry path and read Layout File string from it.

  • Load this dll file from SHGetKnownFolderPath(FOLDERID_System, ...) (usually C:\Windows\System32) with LoadLibrary() call.

  • Next you need to do GetProcAddress(KbdDllHandle, "KbdLayerDescriptor") - you're receive pointer that can be casted to PKBDTABLES.

  • There is kbd.h header in Windows SDK that have KBDTABLES struct definition (there is some stuff involved to use proper KBD_LONG_POINTER size for x32 code running on x64 Windows. See my link to Gtk source at the end).

  • You have to look at pKeyNames and pKeyNamesExt in it to get scan code -> key name mapping.

Long story short: The GTK toolkit have the code that doing all this(see here and here). Actually they are building scan code -> printed chars tables from Windows keyboard layout dlls.

Upvotes: 0

decasteljau
decasteljau

Reputation: 8033

Update: This answer does not work. It actually modifies the keyboard settings of the user. This appear to be a behavior change between Windows versions.

CString csLangId;
csLangId.Format( L"%08X", MAKELANGID( LANG_INVARIANT, SUBLANG_NEUTRAL ) );
HKL hLocale = LoadKeyboardLayout( (LPCTSTR)csLangId, KLF_ACTIVATE );
HKL hPrevious = ActivateKeyboardLayout( hLocale, KLF_SETFORPROCESS );

// Call GetKeyNameText

ActivateKeyboardLayout( hPrevious, KLF_SETFORPROCESS );
UnloadKeyboardLayout( hLocale );

Upvotes: 3

Related Questions