【发布时间】: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