我正在创建一个安装键盘钩子的程序来捕获所有键并显示一些与它们相关的文本。
但是,我遇到了障碍,这是安装挂钩时的一些键更改行为。
我会看到发布一个小而完整的测试程序,但现在我只是描述问题。
问题出现在Windows 7 64位,在.NET 4.0中,一个C#程序。我认为这些都不重要。
我的钩子通过SetWindowsHookEx
安装,然后处理系统中处理的所有密钥。
如果钩子方法只是返回,或者对键进行了最小的处理(我将在一秒内发布改变行为的内容),键盘在程序中按预期运行。
但是,如果我从User32.dll调用此函数ToAscii
,以确定我的键盘OemTilde上的哪个键或类似键,那么“覆盖下一个键”的任何键都会停止运行。我不知道这些键的正确名称,但两个撇号类型,``和', as well as
〜and
¨,停止运作。
例如,如果我点击~
然后点击N
,则显示如下:
有谁知道为什么会发生这种情况以及如何解决这个问题?
现在我只能在其他程序中正确处理密钥,即使这意味着我无法在自己的程序中正确检测正确的密钥序列。
更多信息:
如果我将ToAscii
函数作为钩子方法的一部分调用,则会出现另一个问题。像¨
这样的键被处理两次,即。如果我点击¨
一次,记事本会收到两个¨¨
个字符,现在点击N
只会添加N
。
但是,如果我使用BeginInvoke
来处理单独线程上的键,则在从键盘钩子方法返回后,会出现第一个问题。
我的节目可能有点特别:
因此,我的代码最终看起来如下:
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);
}
答案 0 :(得分:2)
这些密钥称为 dead keys ,您可以通过删除对ToAscii
的调用来解决问题。另请参阅以下相关主题:
<强> ToAscii/ToUnicode in a keyboard hook destroys dead keys. 强>
更新:我没有看到你的代码,但是在处理KeyboardProc
回调函数的参数时,你可以检查当code
参数小于0时你是否传递了键盘消息?文档说:
代码 [in]
int
钩子过程用于确定如何处理消息的代码。 如果代码小于零,则挂钩过程必须将消息传递给
CallNextHookEx
函数而不进行进一步处理,并应返回CallNextHookEx
返回的值。强>
MSDN中有setting up a managed hook的示例:
if (nCode < 0)
{
return CallNextHookEx(hHook, nCode, wParam, lParam);
}
else
{
// process message here
return CallNextHookEx(hHook, nCode, wParam, lParam);
}
答案 1 :(得分:2)
您的问题中缺少一些关键信息,您使用了哪两个键盘钩?简单的一个,WH_KEYBOARD_LL无法正常工作。您将最终使用程序的键盘状态,而不是实际获得击键的程序。死钥匙确实有所作为。
硬件WH_KEYBOARD需要一个无法在托管代码中编写的钩子。您需要一个可以在每个进程中注入的非托管DLL。一旦你得到它,我就不用打扰键盘钩子,也可以用WH_CALLWNDPROC记录WM_CHAR消息。
示例DLL可用here。
答案 2 :(得分:1)
您可能会在这里看到的是在涉及死锁时尝试映射密钥的效果。键盘映射是一个相当复杂的过程,在产生这种行为的某些类型的键周围存在很多陷阱。
我鼓励您阅读Michael Kaplan关于此主题的以下博客系列。它帮我解决了一些错误。
答案 3 :(得分:0)
鉴于目前的答案和对国际行为的提及,您可能需要考虑“代码页”。代码页根据国家/地区而变化。
示例国家/地区代码页