MapVirtualKey 有一个known broken behaviour。
The docs 在 MAPVK_VK_TO_CHAR 或 2 模式上撒谎。
根据实验和leaked Windows XP source code(在\windows\core\ntuser\kernel\xlate.c 文件中),它包含'A'..'Z' VKs 的不同行为(那些VKs 没有在Win32 API WinUser.h 标头中明确定义,等效于' A'..'Z' ASCII 字符):
case 2:
/*
* Bogus Win3.1 functionality: despite SDK documenation, return uppercase for
* VK_A through VK_Z
*/
if ((wCode >= (WORD)'A') && (wCode <= (WORD)'Z')) {
return wCode;
}
不知道为什么 MS 决定从 Win 3.1 中移除这个错误,但我的 Windows 10 上的当前情况是这样的。
此外,some keyboard layouts 可以在单次按键时发出多个 WCHAR 字符(UTF-16 surrogate pairs 或 ligatures 可以包含多个 Unicode 代码点)。 MapVirtualKey 和 MAPVK_VK_TO_CHAR 也无法为这些键返回正确的值 - 在这种情况下它将返回 U+F002 代码点。
作为一种解决方法,我建议您使用可以为您执行此映射的 ToUnicode[Ex] API:
inline std::string ToUnicodeWrapper(uint16_t vkCode, uint16_t scanCode, bool isShift = false)
{
const uint32_t flags = 1 << 2; // Do not change keyboard state of this thread
static uint8_t state[256] = { 0 };
state[VK_SHIFT] = isShift << 7; // Modifiers set the high-order bit when pressed
wchar_t utf16Chars[10] = { 0 };
// This call can produce multiple UTF-16 code points
// in case of ligatures or non-BMP Unicode chars that have hi and low surrogate
// See examples: https://kbdlayout.info/features/ligatures
int charCount = ::ToUnicode(vkCode, scanCode, state, utf16Chars, 10, flags);
// negative value is returned on dead key press
if (charCount < 0)
charCount = -charCount;
// do not return blank space and control characters
if ((charCount == 1) && (std::iswblank(utf16Chars[0]) || std::iswcntrl(utf16Chars[0])))
charCount = 0;
return utf8::narrow(utf16Chars, charCount);
}
甚至更好:如果您有 Win32 消息循环 - 只需使用 TranslateMessage()(在后台调用 ToUnicode())然后处理 WM_CHAR 消息。
PS:同样适用于 GetKeyNameText API,因为它在后台调用 MapVirtualKey(vk, MAPVK_VK_TO_CHAR) 来获取在键盘布局 dll 中未设置明确名称的键(通常只有非字符才有名称)。