键盘挂钩中的 ToAscii/ToUnicode 会破坏死键
看来,如果您在全局 WH_KEYBOARD_LL 挂钩中调用 ToAscii()
或 ToUnicode()
并按下死键,它将被“销毁”。
例如,假设您已在 Windows 中将输入语言配置为西班牙语,并且想要在程序中键入重音字母 á。通常,您会按单引号键(死键),然后按字母“a”,然后屏幕上会按预期显示带重音的 á。
但是,如果您在低级键盘挂钩函数中调用 ToAscii()
或 ToUnicode()
,则此方法不起作用。死键似乎已被破坏,因此屏幕上不会显示重音字母 á。删除对上述函数的调用可以解决问题...但不幸的是,我需要能够调用这些函数。
我google了一下,虽然很多人似乎都有这个问题,但没有提供好的解决方案。
任何帮助将不胜感激!
编辑:我正在调用ToAscii()
来转换我的LowLevelKeyboardProc 挂钩函数到将在屏幕上为用户显示的结果字符。
我尝试了 MapVirtualKey(kbHookData->vkCode, 2),但这并不像 ToAscii() 那样“完整”。例如,如果您按 Shift + 2,您将得到“2”,而不是“@”(或者 Shift + 2 将为用户的键盘布局/语言生成的任何内容)。
ToAscii()
是完美的......直到按下死键。
EDIT2:这是钩子函数,删除了不相关的信息:
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);
}
It seems that if you call ToAscii()
or ToUnicode()
while in a global WH_KEYBOARD_LL hook, and a dead-key is pressed, it will be 'destroyed'.
For example, say you've configured your input language in Windows as Spanish, and you want to type an accented letter á in a program. Normally, you'd press the single-quote key (the dead key), then the letter "a", and then on the screen an accented á would be displayed, as expected.
But this doesn't work if you call ToAscii()
or ToUnicode()
in a low-level keyboard hook function. It seems that the dead key is destroyed, and so no accented letter á shows up on screen. Removing a call to the above functions resolves the issue... but unfortunately, I need to be able to call those functions.
I Googled for a while, and while a lot of people seemed to have this issue, no good solution was provided.
Any help would be much appreciated!
EDIT: I'm calling ToAscii()
to convert the virtual-key code and scan code received in my LowLevelKeyboardProc hook function into the resulting character that will be displayed on screen for the user.
I tried MapVirtualKey(kbHookData->vkCode, 2)
, but this isn't as "complete" a function as ToAscii()
; for example, if you press Shift + 2, you'll get '2', not '@' (or whatever Shift + 2 will produce for the user's keyboard layout/language).
ToAscii()
is perfect... until a dead-key is pressed.
EDIT2: Here's the hook function, with irrelevant info removed:
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);
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
已知
ToUnicode()
及其旧版本的ToAscii()
可以更改当前线程的键盘状态,从而扰乱死键和 ALT+NUMPAD 击键:为了避免这种情况,您可以在单独的线程中执行 ToUnicode() 调用(它将具有单独的键盘状态)或在
wFlags
参数中使用特殊标志,该标志记录在ToUnicode() 文档:或者您可以预先准备 sc->char 映射表并在
WM_INPUTLANGCHANGE
语言更改事件上更新它。我认为它也应该与
ToAscii()
一起使用,但最好不要使用这种旧的 ANSI 代码页相关方法。使用ToUnicode()
API 代替,它甚至可以返回连字 和 UTF-16 代理对 - 如果键盘布局有它们。 有些是。请参阅异步输入与同步输入的快速介绍
这背后的原因。
It is known that
ToUnicode()
and its older counterpartToAscii()
can change keyboard state of the current thread and thus mess with dead keys and ALT+NUMPAD keystrokes:To avoid that you can do your ToUnicode() call in a separate thread (it will have a separate keyboard state) or use a special flag in
wFlags
param that is documented in ToUnicode() docs:Or you can prepare sc->char mapping table beforehand and update it on
WM_INPUTLANGCHANGE
language change event.I think it should work with
ToAscii()
too but better not use this old ANSI codepage-dependant method. UseToUnicode()
API instead that can even return ligatures and UTF-16 surrogate pairs - if keyboard layout have them. Some do.See Asynchronous input vs synchronous input, a quick introduction
for the reason behind this.
相当古老的线程。不幸的是,它不包含我正在寻找的答案,并且所有答案似乎都无法正常工作。我终于通过检查 MSB 解决了这个问题在调用
ToUnicode
/ToAscii
之前,使用MapVirtualKey
函数。似乎工作得像一个魅力:引用 MSDN 关于
MapVirtualKey
的返回值,如果使用MAPVK_VK_TO_CHAR
:Quite an old thread. Unfortunately it didn't contain the answer I was looking for and none of the answers seemed to work properly. I finally solved the problem by checking the MSB of the
MapVirtualKey
function, before callingToUnicode
/ToAscii
. Seems to be working like a charm:Quoting MSDN on the return value of
MapVirtualKey
, ifMAPVK_VK_TO_CHAR
is used:您不应该将按钮与字符组合起来 - 假设任何键/按钮都具有文本表示形式 (Unicode) 是错误的。
因此:
You shouldn't combine the buttons with characters - assuming that any key/button has a text representation (Unicode) is wrong.
So:
我在用 C# 创建键盘记录器时遇到了这个问题,以上答案都不适合我。
经过深入的博客搜索后,我偶然发现了这个键盘监听器,它可以完美地处理死键。
I encountered this issue while creating a key logger in C# and none of the above answers worked for me.
After a deep blog searching, I stumbled across this keyboard listener which handles dead keys perfectly.
这是一个完整的代码,涵盖了使用 ALT + NUMPAD 的死键和快捷键,基本上是 TextField 输入处理的完整实现:
Here is a full code which covers dead keys and shortcut keys using ALT + NUMPAD, basically a full implementation of a TextField input handling:
调用“ToAscii”函数两次以正确处理死键,如下所示:
Call 'ToAscii' function twice for a correct processing of dead-key, like in:
调用
ToAscii
或ToUnicode
两次就是答案。我找到了这个并将其转换为 Delphi,并且它有效!
Calling the
ToAscii
orToUnicode
twice is the answer.I found this and converted it for Delphi, and it works!
在引入
wFlag
参数的位0x04
之前,进行非破坏性ToUnicode
调用 是一个棘手的话题。引用的文本详细讨论了它(以及当前状态)。 (有时我怀疑可能正是这种讨论导致了0x04
的实现......但它也被建议较早。)引用部分的末尾有一个非常简短的“旧方法”摘要:使用
wFlags=0x01|0x02
并自行检测/实现按数字输入。 (与 按数字输入的详细信息。)Before introduction of the bit
0x04
for thewFlag
argument, doing non-destructiveToUnicode
calls was a tricky topic. The referenced text discusses it (and the current state) in a lot of details. (Sometimes I suspect that it may have been this discussion which led to implementation of0x04
… But it was also suggested earlier.)A very short summary “of the old way” is at the end of the referenced section: use
wFlags=0x01|0x02
and detect/implement input-by-number yourself. (Compare with the 4th note in details of input-by-number.)我将 vkCode 复制到队列中并从另一个线程进行转换
这具有不延迟操作系统关键处理的优点
I copy the vkCode in a queue and do the conversion from another thread
This has the advantage of not delaying key processing by the os
这对我有用
This works for me