【问题标题】:C++ ToUnicodeEx() not recognizing shift key unless i have a messgaebox()C++ ToUnicodeEx() 不识别 shift 键,除非我有一个 messgaebox()
【发布时间】:2012-02-21 05:46:15
【问题描述】:

我在用 C++ 编写键盘钩子时遇到了一些麻烦。

我可以读取击键,但我尝试在按下 shift 键时使用 ToUnicodeEx() 转换击键。 在我到目前为止的代码中,我有

i = ToUnicodeEx(keyboard.vkCode, keyboard.scanCode, (PBYTE)&keyState, (LPWSTR)&keybuff, sizeof(keybuff) / 16, 0,keyboardlayout);
MessageBox(MainnhWnd,keybuff, L"message", MB_OK | MB_ICONEXCLAMATION);

当我按下 Shift+2 时,这个“MessageBox”行会弹出两个消息框,第一个是 Shift 键的空白,第二个显示一个“@”字符。这是意料之中的。

但如果我删除此消息框,ToUnicodeEx() 函数会转换击键,就好像没有使用 shift 键一样。我可以通过设置断点、命中计数器或将字符输出到程序窗口中的编辑框来看到这一点。

此外,当我包含 MsgBox 行并使用 CapLock 时,我的字母会相应更改,但是在我删除 msgbox 行后,它仅在我的程序启动时使用大写锁定状态(程序启动时大写锁定打开,所有字母都是大写,反之亦然,当程序启动时所有字母都是小写,即使我改变了大写锁定状态)

任何人都知道为什么我的钩子只记得启动时的键盘状态,除非我包含 msgbox?

我的钩子设置如下:

theHook = SetWindowsHookEx ( WH_KEYBOARD_LL, (HOOKPROC) KeyEvent, exe, 0);

而我的钩子回调函数是:

DLLEXPORT LRESULT CALLBACK KeyEvent(int nCode, WPARAM wParam, LPARAM lParam) {
if (nCode>=0) {
    int i = 0;
    KBDLLHOOKSTRUCT keyboard;
    WCHAR keybuff[256]= {0};

    if ((wParam == WM_KEYDOWN)|| (wParam == WM_SYSKEYDOWN)||(wParam == WM_SYSKEYUP)) {
        keyboard = *((KBDLLHOOKSTRUCT*)lParam);
        if (keyboard.vkCode == VK_RETURN) {
            i += wsprintf (((LPWSTR)keybuff + i),L"[Return]\r\n");
        }
        else {
            HKL keyboardlayout = GetKeyboardLayout(0);
            GetKeyboardState((PBYTE)&keyState);
            i = ToUnicodeEx(keyboard.vkCode, keyboard.scanCode, (PBYTE)&keyState, (LPWSTR)&keybuff, sizeof(keybuff) / 16, 0,keyboardlayout);

            MessageBox(MainnhWnd,keybuff, L"message", MB_OK | MB_ICONEXCLAMATION);
        }
        if (keybuff>0) {
            addToEditBox(keybuff);
        }
    }
}
return CallNextHookEx(theHook, nCode, wParam, lParam);
}

【问题讨论】:

  • GetKeyboardLayout 和 GetKeyState 返回你的进程的键盘布局和状态。不是那个会收到键盘消息的人。你不能可靠地使用 ToUnicodeEx(),需要一个带有 WH_KEYBOARD 的全局钩子。
  • @HansPassant 我认为你可以。这是一个全局挂钩,因此函数位于 DLL 中,并且该 DLL 被 注入 到原始进程中。所以这些功能应该可以按预期工作。
  • @rodrigo - WH_KEYBOARD_LL 不是全局挂钩。没有注入 DLL,Windows 在将消息添加到具有前台的窗口的消息队列之前,会在您自己的进程中调用您的钩子回调。 OP 发现 MessageBox 有效,因为它将焦点从该窗口移开。
  • @HansPassant - 实际上,根据MSDN,WH_KEYBOARD_LL 是一个全局挂钩。可能底层钩子和其他全局钩子不同,不需要DLL(我没有测试过)。但我在文档中没有看到任何对此的引用。

标签: c++ visual-studio-2010 hook


【解决方案1】:

根据ToUnicodeEx function 的文档,您应该提供一个指向包含当前键盘状态的 256 字节数组的指针 (const BYTE *lpKeyState)。数组中的每个元素(字节)都包含一个键的状态。如果设置了一个字节的高位,则key为down。

在你调用ToUnicodeEx之前,你应该像这样设置这个数组(伪代码):

enum Keys
{
    ShiftKey    = 16, // shift
    ControlKey  = 17, // ctrl
    Menu        = 18, // alt
    Capital     = 20, // caps lock
};

BYTE keyState[256]= {0};

if (Control key down)
    keyState[Keys::ControlKey] = 0x80;

if (Shift key down)
    keyState[Keys::ShiftKey] = 0x80;

if (Alt key down)
    keyState[Keys::Menu] = 0x80;

if (Caps lock ON)
    keyState[Keys::Capital] = 0x01;

而当keyState数组被设置后,就可以调用:

ToUnicodeEx(keyboard.vkCode, keyboard.scanCode, (PBYTE)&keyState, (LPWSTR)&keybuff, sizeof(keybuff) / 16, 0,keyboardlayout);

我一直在使用 ToUnicodeEx 函数,就像这样,它运行良好。
希望这会对你有所帮助;)

【讨论】:

  • 谢谢,效果很好。我认为这与 keyState 变量有关,但我认为 GetKeyboardState() 更新了它。现在我只需要识别大写锁定是否处于活动状态,但这应该不会太难。
猜你喜欢
  • 2016-06-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多