4

我正在创建一个程序,它安装一个键盘钩子来捕获所有键并显示一些与它们相关的文本。

但是,我遇到了一个障碍,那就是安装钩子时一些键会改变行为。

我会看到发布一个小而完整的测试程序,但现在我只描述这个问题。

该问题出现在 .NET 4.0 中的 Windows 7 64 位 C# 程序中。我认为这些都不重要。

我的钩子通过安装自己SetWindowsHookEx,然后处理系统中处理的所有密钥。

如果钩子方法只是简单地返回,或者对键进行最少的处理(我将在一秒钟内发布改变行为的内容),那么键盘将按程序中的预期运行。

但是,如果我ToAscii从 User32.dll 调用此函数来确定我的键盘 OemTilde 或类似键上的哪个键,那么任何“覆盖下一个键”的键都会停止运行。我不知道这些键的正确名称,但是两种撇号类型,`` 和 ´ , as well as~ and¨`,停止工作。

例如,如果我点击~then 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);
}
4

4 回答 4

2

您的问题缺少关键信息,您使用两个键盘挂钩中的哪一个?最简单的 WH_KEYBOARD_LL 不能工作。您最终将使用程序的键盘状态,而不是实际获得击键的程序。死键确实有所作为。

最难的 WH_KEYBOARD 需要一个无法在托管代码中编写的钩子。您需要一个可以在每个进程中注入的非托管 DLL。一旦你明白了,我就不用担心键盘钩子了,还不如用 WH_CALLWNDPROC 记录 WM_CHAR 消息。

此处提供了一个示例 DLL 。

于 2010-08-23T18:22:31.330 回答
2

这些键称为死键,您可以通过删除对ToAscii. 另请参阅以下相关线程:

键盘挂钩中的 ToAscii/ToUnicode 会破坏死键。

更新:我没有看到你的代码,但是在处理KeyboardProc回调函数的参数时,你能检查一下你在code参数小于0时传递了键盘消息吗?文档说:

代码[输入] int

挂钩过程用于确定如何处理消息的代码。如果code小于零,则挂钩过程必须将消息传递给CallNextHookEx函数而不进行进一步处理,并且应该返回由CallNextHookEx.

在 MSDN 中有一个用于设置托管挂钩的示例:

if (nCode < 0)
{
    return CallNextHookEx(hHook, nCode, wParam, lParam);
}
else
{
    // process message here

    return CallNextHookEx(hHook, nCode, wParam, lParam); 
}
于 2010-08-23T15:26:18.203 回答
1

您可能在这里看到的是在涉及死键时尝试映射键的效果。键盘映射是一个相当复杂的过程,在产生这种行为的某些类型的键周围有很多陷阱。

我鼓励您阅读 Michael Kaplan 关于该主题的以下博客系列。它帮助我解决了一些错误。

于 2010-08-23T15:22:26.560 回答
0

鉴于当前的答案和对国际行为的引用,您可能需要考虑“代码页”。代码页因国家/地区而异。

示例国家代码页

  • 美国、英国 437
  • 多语言 850
  • 斯拉夫语 852
  • 葡萄牙语 860
  • 冰岛语 861
  • 加拿大人、法国人 863
  • 斯堪的纳维亚/北欧 865

更多信息

MSDN 信息

Windows 应用程序的国际化

于 2010-08-23T15:57:18.427 回答