为何MapVirtualKey无法返回扩展键标志?
我尝试通过PostMessage()合成按键输入,需要生成对应的lParam参数,且熟悉KeyStrokeMessage结构体。相关代码如下:
///------------------------------------------------------------------------------------------- /// 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; }
拦截真实按键排查问题时发现,合成VK_UP(上箭头)按键时,真实消息里wParam为VK_UP,lParam的扫描码是0x48且扩展标志已设置,对应扫描码应为0xE048。但MapVirtualKey()返回的是72(0x48),导致无法设置ExtendedFlag。
根据MSDN文档,MapVirtualKey应该返回扩展标志,我已经尝试了MapVirtualKey()、MapVirtualKeyEx()(使用实际键盘布局及默认0值),用了正确的MAPVK_VK_TO_VSC_EX,也试过MAPVK_VK_TO_VSC;甚至从真实扫描码反向转换为虚拟键再转回扫描码,仍无法获取扩展标志。经键盘布局验证,VK_UP的扫描码确实应为0xE048。请问如何让MapVirtualKeyA()返回扩展标志?
手动标记扩展键
部分导航键(如VK_UP、VK_DOWN、VK_LEFT、VK_RIGHT,以及VK_INSERT、VK_DELETE等)的扩展标志无法通过MapVirtualKey直接获取,这是因为函数行为依赖当前键盘布局和系统设置。可以手动为这些键设置扩展标志:
修改GetKeyMessageLPARAM函数,增加对特定虚拟键的判断:
LPARAM GetKeyMessageLPARAM(unsigned aVKey, bool aIsDown, bool aIsSystem) { KeyLParam lp; UINT sc = MapVirtualKeyA(aVKey, MAPVK_VK_TO_VSC_EX); bool isExtended = false; // 手动标记需要扩展标志的虚拟键 switch(aVKey) { case VK_UP: case VK_DOWN: case VK_LEFT: case VK_RIGHT: case VK_INSERT: case VK_DELETE: case VK_HOME: case VK_END: case VK_PRIOR: case VK_NEXT: isExtended = true; break; default: isExtended = (HIBYTE(sc) == 0xE0 || HIBYTE(sc) == 0xE1); break; } lp.RepeatCount = 1; lp.ScanCode = LOBYTE(sc); lp.ExtendedFlag = isExtended ? 1 : 0; lp.Reserved = 0; lp.ContextCode = aIsSystem ? 1 : 0; lp.PreviousKeyState = aIsDown ? 0 : 1; lp.TransitionState = aIsDown ? 0 : 1; return *(LPARAM*)&lp; }
验证扩展扫描码的替代方法
如果需要更准确的扫描码(包含扩展位),可以使用GetKeyboardState结合ToAsciiEx反向推导,但这种方法复杂度较高。对于大多数场景,手动标记扩展键的方式足够可靠,因为这些键的扩展属性是固定的系统行为。
内容的提问来源于stack exchange,提问作者Delta

