如何修改Windows全局键盘钩子代码实现扫码输入内容替换?
解决方案思路
核心是缓存扫描器的连续输入字符,识别到目标字符串后拦截原始输入,仅模拟输出需要的部分。因为扫描器是模拟键盘快速输入,可通过输入间隔或结束符(如回车)判断扫描完成。
关键代码修改步骤
假设你已有全局键盘钩子的基础框架,以下是KeyboardHookProc的修改逻辑:
- 定义全局变量缓存输入、记录输入时间戳:
std::wstring inputBuffer; DWORD lastKeyTime = 0; const std::wstring TARGET_STRING = L"abc,def"; const std::wstring OUTPUT_STRING = L"def";
- 在钩子回调中处理按键:
LRESULT CALLBACK KeyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam) { if (nCode >= 0 && (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN)) { KBDLLHOOKSTRUCT* pKeyBoard = (KBDLLHOOKSTRUCT*)lParam; DWORD currentTime = GetTickCount(); // 若两次按键间隔超过200ms,视为新的输入,清空缓冲区(区分手动输入和扫描器快速输入) if (currentTime - lastKeyTime > 200) { inputBuffer.clear(); } lastKeyTime = currentTime; // 获取按键对应的字符(处理Shift等修饰键) wchar_t ch = 0; BYTE keyState[256] = {0}; GetKeyboardState(keyState); ToUnicode(pKeyBoard->vkCode, pKeyBoard->scanCode, keyState, &ch, 1, 0); if (ch != 0) { inputBuffer += ch; // 检查是否匹配目标字符串(含可能的回车结束符) if (inputBuffer == TARGET_STRING || inputBuffer == TARGET_STRING + L"\r") { // 模拟输出"def" INPUT inputs[3] = {0}; for (int i = 0; i < OUTPUT_STRING.length(); i++) { inputs[i].type = INPUT_KEYBOARD; inputs[i].ki.wVk = 0; inputs[i].ki.wScan = OUTPUT_STRING[i]; inputs[i].ki.dwFlags = KEYEVENTF_UNICODE; } SendInput(OUTPUT_STRING.length(), inputs, sizeof(INPUT)); // 清空缓冲区并拦截原始输入 inputBuffer.clear(); return 1; } // 若缓冲区前缀不匹配目标,正常传递消息并清空缓存 else if (TARGET_STRING.find(inputBuffer) != 0) { inputBuffer.clear(); } } } return CallNextHookEx(NULL, nCode, wParam, lParam); }
针对三种尝试的问题分析
- 若第一种是逐字符拦截:没有缓存完整字符串就提前处理,无法识别完整的"abc,def",易出现误拦截或放行。
- 若第二种是直接替换单个字符:扫描器输入是连续按键,单个字符无法判断是否属于目标前缀,容易误替换普通输入。
- 若第三种是未正确拦截原始消息:即使模拟输出了"def",原始的"abc,def"仍会被输入,导致内容重复。
注意事项
- 处理扫描器结束符:部分扫描器会在扫描后自动添加回车(
\r或\n),需在目标判断中包含该情况。 - 权限要求:全局键盘钩子需要程序以管理员权限运行,避免被系统拦截。
- 修饰键兼容:
ToUnicode会自动处理Shift等按键,确保正确获取输入字符(如大写字母)。
内容的提问来源于stack exchange,提问作者Ari
相关产品推荐
相关产品推荐

