键盘钩子改变按键的行为
我正在创建一个程序,该程序安装一个键盘挂钩来捕获所有按键并显示一些与它们相关的文本。
然而,我遇到了一个障碍,那就是安装挂钩后一些按键会改变行为。
我会考虑发布一个小型但完整的测试程序,但现在我只描述问题。
该问题出现在 Windows 7 64 位、.NET 4.0(一个 C# 程序)中。我认为这些都不重要。
我的钩子通过 SetWindowsHookEx 自行安装,然后处理系统中处理的所有密钥。
如果钩子方法只是返回,或者对键进行最少的处理(我将在一秒钟内发布改变行为的内容),则键盘将按程序中的预期运行。
但是,如果我从 User32.dll 调用此函数 ToAscii
来确定键盘上的 OemTilde 或类似键实际上是哪个键,则任何“覆盖下一个键”的键都会停止运行。我不知道这些键的正确名称,但是两个撇号类型“和”,以及
~和
“,停止工作。
例如,如果我点击 ~
然后点击 N
,它会显示如下:
- 没有安装键盘钩子: ñ
- 安装键盘钩子: n (注意上面没有 ~)
有没有人知道为什么会发生这种情况以及如何解决这个问题?
现在,我将满足于在其他程序中正确处理按键,即使这意味着我无法在自己的程序中正确检测到正确的按键序列。
更多信息:
如果我调用 ToAscii
函数作为挂钩方法的一部分,则会出现不同的问题。像 ¡
这样的键被处理两次,即。如果我点击一次 ¡
,记事本会收到两个 ¡¡
字符,点击 N
现在只会添加 N
。
但是,如果我使用 BeginInvoke
在单独的线程上处理按键,则从键盘钩子方法返回后,会出现第一个问题。
我的程序可能有点特别:
- 我不使用键盘状态(即,我传递的“键状态”256 字节数组只是充满 0)
- 我不关心死键(在某种意义上)我的程序不会处理它们,我非常关心它们,我不希望我的程序使它们对系统的其余部分毫无用处)
因此,我的代码最终如下所示:
private bool IsDeadKey(uint key)
{
return ((Hook.Interop.MapVirtualKey(key, 2) & 2147483648) == 2147483648);
}
void _Hook_KeyDown_Async(KeyDownEventArgs e)
{
var inBuffer = new byte[2];
char key = '\0';
if (!IsDeadKey((uint)e.KeyCode))
{
int ascii = Hook.Interop.ToAscii((int) e.KeyCode,
e.ScanCode,
_KeyState,
inBuffer,
e.Flags);
if (ascii == 1)
{
key = Char.ToUpper((char) inBuffer[0]);
}
}
BeginInvoke(
new Action<Keys, Boolean, Boolean, Boolean, Char>(ProcessKeyboardEvent),
e.KeyCode, e.Control, e.Shift, e.Alt, key);
}
I'm creating a program that installs a keyboard hook to capture all keys and display some text related to them.
However, I've hit upon a snag, and that is some keys change behavior when the hook is installed.
I'll see about posting a small, but complete, test program, but for now I'll just describe the problem.
The problem exhibits itself on Windows 7 64-bit, in .NET 4.0, a C# program. I assume none of this matters.
My hook installs itself via SetWindowsHookEx
and then handles all keys processed in the system.
If the hook method simply returns, or does minimal processing of the key (I'll post what changes the behavior in a second), the keyboard functions as expected in programs.
However, if I call this function, ToAscii
from User32.dll, to figure out which key on my keyboard OemTilde or similar really is, then any key that "overlays the next key" stops functioning. I don't know the correct name for such keys, but the two apostrophe-types, `` and ´, as well as
~and
¨`, stops functioning.
For instance, if I hit ~
and then N
, it displays as follows:
- Without keyboard hook installed: ñ
- With keyboard hook installed: n (notice no ~ above)
Does anyone know why this happens and how I can fix this problem?
For now I'll settle for just handling the keys correctly in other programs, even if that means that I won't be able to correctly detect the right key sequences in my own program.
Some more information:
If I call the ToAscii
function as part of the hook method, then a different problem occurs. Keys like ¨
are processed twice, ie. if I hit the ¨
once, Notepad receives two ¨¨
characters, and hitting N
now just adds the N
.
However, if I use BeginInvoke
to process the key on a separate thread, after returning from the keyboard hook method, the first problem occurs.
My program is probably a bit special in that:
- I don't use keyboard state (ie. the "Key state" 256-byte array I pass around is just full of 0's)
- I don't care about dead keys (in the sense that my program won't process them, I care just enough about them that I don't want my program to render them useless to the rest of the system)
As such, my code ended up looking as follows:
private bool IsDeadKey(uint key)
{
return ((Hook.Interop.MapVirtualKey(key, 2) & 2147483648) == 2147483648);
}
void _Hook_KeyDown_Async(KeyDownEventArgs e)
{
var inBuffer = new byte[2];
char key = '\0';
if (!IsDeadKey((uint)e.KeyCode))
{
int ascii = Hook.Interop.ToAscii((int) e.KeyCode,
e.ScanCode,
_KeyState,
inBuffer,
e.Flags);
if (ascii == 1)
{
key = Char.ToUpper((char) inBuffer[0]);
}
}
BeginInvoke(
new Action<Keys, Boolean, Boolean, Boolean, Char>(ProcessKeyboardEvent),
e.KeyCode, e.Control, e.Shift, e.Alt, key);
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
这些密钥称为死密钥,您也许可以解决通过删除对
ToAscii
的调用来解决这个问题。另请参阅以下相关主题:更新:我还没有看到你的代码,但是在处理
KeyboardProc
回调函数,可以检查一下当code
时传递键盘消息吗参数小于0?文档说:MSDN 中有一个设置托管挂钩的示例:
These keys are called dead keys and you might be able to solve the problem by removing the call to
ToAscii
. See also the following related thread:Update: I haven't seen your code, but when processing the parameters of the
KeyboardProc
callback function, can you check that you pass the keyboard message on when thecode
parameter is less than 0? The documentation says:There is a sample for setting up a managed hook in MSDN:
您的问题缺少一个关键信息,您使用两个键盘挂钩中的哪一个?最简单的是,WH_KEYBOARD_LL 无法工作。您最终将使用程序的键盘状态,而不是实际获取击键的程序。死键确实会带来不同。
最困难的是,WH_KEYBOARD 需要一个无法用托管代码编写的挂钩。您需要一个可以注入到每个进程中的非托管 DLL。一旦你明白了,我就不再费心使用键盘钩子,还不如用 WH_CALLWNDPROC 记录 WM_CHAR 消息。
此处提供了示例 DLL。
A critical bit of information is missing from your question, which of the two keyboard hooks do you use? The easy one, WH_KEYBOARD_LL cannot work. You'll end up using the keyboard state of your program, not the program that actually gets the keystroke. Dead keys indeed make the difference.
The hard one, WH_KEYBOARD requires a hook that you cannot write in managed code. You'll need an unmanaged DLL that can be injected in every process. Once you got that, I'd just not bother with a keyboard hook, might as well log the WM_CHAR messages with WH_CALLWNDPROC.
A sample DLL is available here.
您可能在这里看到的是当涉及死键时尝试映射键的效果。键盘映射是一个相当复杂的过程,在产生这种行为的某些类型的按键周围存在很多陷阱。
我鼓励您阅读以下由 Michael Kaplan 撰写的关于该主题的博客系列。它帮助我解决了许多错误。
What you're likely seeing here is the effect of trying to map a key when a dead key is involved. Keyboard mapping is a fairly involved process that has a lot of pitfalls around certain types of keys which produce this behavior.
I encourage you to read the following blog series by Michael Kaplan on the subject. It helped me sort out a number of bugs.
鉴于当前的答案和对国际行为的参考,您可能需要考虑“代码页”。代码页根据国家/地区而变化。
国家代码页示例
更多信息
MSDN 信息
Windows 应用程序国际化
Given the current answers and the reference to international behavior, you may need to account for "codepages." Codepages change based upon country.
Example Country Codepages
More Info
MSDN Info
Internationalization for Windows Applications