我正在使用ToUnicodeEx
函数,它需要键盘状态作为输入参数。所以,我使用GetKeyboardState
函数来做到这一点。但是我注意到当我用键盘键输入键组合时,如 SHIFT + A ,有一个字符延迟。这是一个例子。
aaa(现在持有 SHIFT )aAAAAAAA(发布 SHIFT )Aaaa
在调试时,我注意到GetKeyboardState
导致此延迟。我该如何处理或防止这种延迟?
这是我的整个键盘钩子过程。
void proc(KBDLLHOOKSTRUCT kbdStruct) {
fdebug = fopen("debug.txt", "a");
foutput= fopen("output.txt", "a");
WCHAR pwszBuff[9];
WCHAR key[9];
char str[8];
BOOL isDead = FALSE;
BYTE lpKeyState[256];
HWND currentHwnd = GetForegroundWindow();
LPDWORD currentProcessID = 0;
DWORD currentWindowThreadID = GetWindowThreadProcessId(currentHwnd, currentProcessID);
DWORD thisProgramThreadId = GetCurrentThreadId();
hkl = GetKeyboardLayout(thisProgramThreadId);
if (AttachThreadInput(thisProgramThreadId, currentWindowThreadID, TRUE))
{
GetKeyboardState(lpKeyState);
AttachThreadInput(thisProgramThreadId, currentWindowThreadID, FALSE);
}
else
{
GetKeyboardState(lpKeyState);
}
int ret = ToUnicodeEx(kbdStruct.vkCode, kbdStruct.scanCode, lpKeyState, pwszBuff, 8, 0, hkl);
fprintf(fdebug, "vkCode: %d\n", (int)kbdStruct.vkCode);
fprintf(fdebug, "ret: %d\n", (int)ret);
fprintf(fdebug, "lastIsDead: %d\n", (int)lastIsDead);
fprintf(fdebug, "lastIsMod: %d\n", (int)lastIsMod);
fprintf(fdebug, "lastVKCode: %d\n", (int)lastVKCode);
if (ret == -1) {
isDead = TRUE;
ClearKeyboardBuffer(kbdStruct.vkCode, kbdStruct.scanCode, hkl);
}
else if (ret == 0) {
}
else {
memcpy(&key, &pwszBuff, sizeof(pwszBuff));
WideCharToMultiByte(CP_UTF8, 0, key, -1, str, sizeof(str), NULL, NULL);
fprintf(fdebug, "str: %s\n", str);
}
if (lastVKCode != 0 && lastIsDead == TRUE) {
ToUnicodeEx(lastVKCode, lastScanCode, lastKeyState, pwszBuff, 4, 0, hkl);
memcpy(&key, &pwszBuff, sizeof(pwszBuff));
WideCharToMultiByte(CP_UTF8, 0, key, -1, str, sizeof(str), NULL, NULL);
fprintf(fdebug, "str: %s\n", str);
lastVKCode = 0;
}
fprintf(fdebug, "%s", "---------------------------------------------------\n");
fprintf(foutput, "LSHIFT: %d\n", (int)lpKeyState[160]);
fprintf(foutput, "RSHIFT: %d\n", (int)lpKeyState[161]);
fprintf(foutput, "%s", "---------------------------------------------------\n\n");
lastVKCode = kbdStruct.vkCode;
lastScanCode = kbdStruct.scanCode;
lastIsDead = isDead;
fclose(fdebug);
fclose(foutput);
}
这是hookcallback
的更新版本,感谢Ton Plooij。但是,我仍有同样的问题。
LRESULT __stdcall HookCallback(int nCode, WPARAM wParam, LPARAM lParam)
{
LRESULT ret = CallNextHookEx(_hook, nCode, wParam, lParam);
if (nCode >= 0)
{
if (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN)
{
hookStruct = *((KBDLLHOOKSTRUCT*)lParam);
proc(hookStruct);
}
}
return ret;
}
答案 0 :(得分:3)
AttachThreadInput(thisProgramThreadId, currentWindowThreadID, FALSE);
这不符合你的期望。有时。调用GetKeyboardState()时获取正确的值是一种勇敢和必要的努力。花了一段时间才找到失败模式,它根本不明显,我无法让代码以同样的方式失败。当GUI进程位于前台时,它可以正常工作,例如使用Notepad或VS进行尝试。
但是当它是控制台模式进程时不是。
解释这有点令人费解,实际上GetWindowThreadProcessId()会返回误导性信息。它太难以保持这种错觉,即它是拥有控制台窗口的控制台进程。它没有,它实际上是拥有它的关联conhost.exe进程。在旧的Windows版本上是csrss.exe,在Windows 7中添加了conhost.exe来解决由UIPI(又名UAC)引起的拖放问题。
但没有任何体面的方法来发现拥有该窗口的特定conhost.exe进程,更不用说线程ID了。 this Q+A的主题。非常怀疑它会帮助你。
只有好的建议才是众所周知的令人不快的建议:如果你需要可靠地翻译击键,那么你需要使用WH_KEYBOARD钩子而不是WH_KEYBOARD_LL。因此,GetKeyboardState()始终准确,钩子回调在进程中运行。
答案 1 :(得分:1)
LowLevelKeyboardProc在其wParam中接收WM_KEYUP和WM_KEYDOWN消息。您可以自己简单地跟踪按下的修改键,在这种情况下,可以检测向下和向下移动。将键状态信息存储在静态变量中,并使用它来测试在处理其他键时是否按下shift而不是使用GetKeyState。
答案 2 :(得分:0)
您可以尝试GetAsyncKeyState()。 我的键盘钩子模块中的这个功能完美地工作。
https://msdn.microsoft.com/en-us/library/windows/desktop/ms646293(v=vs.85).aspx
按键时,此功能似乎可以捕获硬件中断。
您的GetKeyboardState与Windows注册表有关。 它似乎与下面有关:
“HKEY_USERS \ DEFAULT \ ControlPanel \ Keyboard”,将“KeyboardDelay”值修改为0(默认值为1) 并将“KeyboardSpeed”值更改为48(dafault 31)。
答案 3 :(得分:-1)
根据Hans Passant的回答,我搜索了如何从GetWindowThreadProcessId()
获得正确的值并成功而不是每次在钩子回调中获得hWnd
和windowThreadID
,我得到这些值程序启动后的全局变量,并在钩子回调中使用变量。