Reputation: 8033
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
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:
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
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