在键盘挂钩ToAscii / ToUnicode销毁死锁

看起来,如果在全局WH_KEYBOARD_LL挂钩中调用ToAscii()ToUnicode() ,并且按下死锁,它将被“销毁”。

例如,假设您已经在Windows中将您的input语言configuration为西class牙语,并且您希望在程序中input重音字母á 。 通常情况下,按下单引号键(死键),然后按字母“a”,然后在屏幕上按预期显示重音符号。

但是,如果在低级键盘钩子函数中调用ToAscii()ToUnicode() ,则这不起作用。 看来这个死钥匙被毁了,所以屏幕上没有任何重音字母。 取消对上述function的调用解决了这个问题…但不幸的是,我需要能够调用这些function。

我google了一会儿,虽然很多人似乎有这个问题,没有提供好的解决scheme。

任何帮助将非常感激!

编辑:我打电话ToAscii()将虚拟键代码和我的LowLevelKeyboardProc挂钩函数中收到的扫描代码转换为将在屏幕上显示为用户所产生的字符。

我尝试过MapVirtualKey(kbHookData->vkCode, 2) ,但这不像ToAscii()那样“完整”。 例如,如果按Shift + 2,则会得到“2”,而不是“@”(或Shift + 2将为用户的键盘布局/语言生成的任何内容)。

ToAscii()是完美的…直到一个ToAscii()被按下。

编辑2:这是钩子函数,删除了不相关的信息:

 LRESULT CALLBACK keyboard_LL_hook_func(int code, WPARAM wParam, LPARAM lParam) { LPKBDLLHOOKSTRUCT kbHookData = (LPKBDLLHOOKSTRUCT)lParam; BYTE keyboard_state[256]; if (code < 0) { return CallNextHookEx(keyHook, code, wParam, lParam); } WORD wCharacter = 0; GetKeyboardState(&keyboard_state); int ta = ToAscii((UINT)kbHookData->vkCode, kbHookData->scanCode, keyboard_state, &wCharacter, 0); /* If ta == -1, a dead-key was pressed. The dead-key will be "destroyed" * and you'll no longer be able to create any accented characters. Remove * the call to ToAscii() above, and you can then create accented characters. */ return CallNextHookEx(keyHook, code, wParam, lParam); } 

  1. 停止使用ToAscii()并使用ToUncode()
  2. 请记住,ToUnicode可能在死锁上没有任何回报 – 这就是为什么他们被称为死钥匙。
  3. 任何密钥都有一个扫描码或一个虚拟键码,但不需要一个字符。

你不应该把按钮和字符组合起来 – 假设任何按键/按钮都有文本表示(Unicode)是错误的。

所以:

  • 输入文本使用Windows报告的字符
  • 对于检查按钮按下 (如游戏)使用扫描码虚拟键 (可能虚拟键更好)。
  • 键盘快捷键使用虚拟键码。

调用“ToAscii”函数两次,以正确处理死锁,如下所示:

 int ta = ToAscii((UINT)kbHookData->vkCode, kbHookData->scanCode, keyboard_state, &wCharacter, 0); int ta = ToAscii((UINT)kbHookData->vkCode, kbHookData->scanCode, keyboard_state, &wCharacter, 0); If (ta == -1) ... 

调用ToAsciiToUnicode两次是答案。 我发现这个,并将其转换为Delphi,它的工作原理!

 cnt:=ToUnicode(VirtualKey, KeyStroke, KeyState, chars, 2, 0); cnt:=ToUnicode(VirtualKey, KeyStroke, KeyState, chars, 2, 0); //yes call it twice 

相当古老的线程。 不幸的是,它没有包含我正在寻找的答案,没有一个答案似乎正常工作。 我最终通过在调用ToUnicode / ToAscii之前检查MapVirtualKey函数的MSB来解决这个问题。 似乎像一个魅力工作:

 if(!(MapVirtualKey(kbHookData->vkCode, MAPVK_VK_TO_CHAR)>>(sizeof(UINT)*8-1) & 1)) { ToAscii((UINT)kbHookData->vkCode, kbHookData->scanCode, keyboard_state, &wCharacter, 0); } 

如果使用MapVirtualKey ,则在MAPVK_VK_TO_CHAR的返回值上引用MSDN:

[…]通过设置返回值的最高位来表示死键(变音符号)。 […]

我将vkCode复制到队列中,并从另一个线程进行转换

 @HOOKPROC def keyHookKFunc(code,wParam,lParam): global gkeyQueue gkeyQueue.append((code,wParam,kbd.vkCode)) return windll.user32.CallNextHookEx(0,code,wParam,lParam) 

这样做的好处是不会延迟os的关键处理