SendInput()在记事本中功能异常的技术排查求助
问题分析与解决方案:SendInput注入字符串在记事本中异常
问题概述
- 基于WinAPI实现钩子程序,通过
SetWindowsHookExW()安装全局钩子,捕获LeftAlt+Q或LeftAlt+A组合键,触发时调用SendInput()注入字符串ABCD1234 - 多数Windows 11应用(WordPad、Notepad++、Chrome、Edge等)运行正常,但记事本出现输入异常:
- 初期:仅显示2-4个字符,剩余字符需按额外按键才会分批弹出
- 当前:间歇式每次显示2个字符,速率和光标闪烁同步
- 额外测试:部分Win11设备无此问题,用AutoHotkey配置同款热键+注入操作,在记事本中也出现完全相同的异常
可能原因
- 记事本的输入处理逻辑特殊:作为Windows原生轻量文本编辑器,它对输入事件的缓冲、解析优先级和其他第三方应用不同,尤其当Alt键状态未及时重置时,会阻塞后续输入事件的处理
- SendInput事件序列未同步:注入字符前如果没确保Alt键已完全释放,记事本会误以为仍处于Alt快捷键触发状态,导致输入事件被延迟或拦截
- Win11输入子系统差异:部分Win11版本的输入栈(比如适配触摸/笔输入的兼容层)对钩子注入的事件有节流机制,记事本作为原生应用受影响更显著
可行解决方案
1. 先重置Alt键状态
在调用SendInput()注入字符前,主动发送LeftAlt的释放事件,确保记事本退出Alt键的特殊处理模式:
// 发送LeftAlt释放事件 INPUT releaseAlt = {0}; releaseAlt.type = INPUT_KEYBOARD; releaseAlt.ki.wVk = VK_LMENU; releaseAlt.ki.dwFlags = KEYEVENTF_KEYUP; SendInput(1, &releaseAlt, sizeof(INPUT));
2. 拆分字符注入并加微小延迟
不要一次性注入所有字符,给每个字符的输入事件加10ms以内的延迟,适配记事本的输入处理速率:
const char* injectStr = "ABCD1234"; for (size_t i = 0; injectStr[i] != '\0'; ++i) { INPUT inputPair[2] = {0}; // 按下字符(Unicode模式) inputPair[0].type = INPUT_KEYBOARD; inputPair[0].ki.wVk = 0; inputPair[0].ki.wScan = injectStr[i]; inputPair[0].ki.dwFlags = KEYEVENTF_UNICODE; // 释放字符 inputPair[1].type = INPUT_KEYBOARD; inputPair[1].ki.wVk = 0; inputPair[1].ki.wScan = injectStr[i]; inputPair[1].ki.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP; SendInput(2, inputPair, sizeof(INPUT)); Sleep(8); // 短延迟适配记事本处理节奏 }
3. 针对记事本改用PostMessage发送WM_CHAR
记事本对WM_CHAR消息的处理更直接,绕过钩子事件队列的限制,直接给前台记事本窗口发字符消息:
HWND foregroundWnd = GetForegroundWindow(); // 先判断是否是记事本窗口,避免影响其他应用 if (IsWindow(foregroundWnd)) { wchar_t className[256]; GetClassNameW(foregroundWnd, className, 256); if (wcscmp(className, L"Notepad") == 0) { const wchar_t* injectStr = L"ABCD1234"; for (size_t i = 0; injectStr[i] != L'\0'; ++i) { PostMessageW(foregroundWnd, WM_CHAR, injectStr[i], 0); Sleep(5); } return; // 跳过SendInput逻辑 } } // 其他应用仍用原SendInput逻辑
4. 确保钩子拦截原始按键事件
在钩子回调中,捕获到目标组合键后,返回1标记事件已处理,避免原始Alt+字母按键传递给记事本造成干扰:
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) { if (nCode >= 0 && wParam == WM_KEYDOWN) { KBDLLHOOKSTRUCT* keyData = (KBDLLHOOKSTRUCT*)lParam; // 检测LeftAlt+Q/A组合键 if ((GetKeyState(VK_LMENU) & 0x8000) && (keyData->vkCode == 'Q' || keyData->vkCode == 'A')) { // 执行注入逻辑... return 1; // 拦截原始按键,不让记事本收到 } } return CallNextHookEx(NULL, nCode, wParam, lParam); }
内容的提问来源于stack exchange,提问作者Kalle Svensson
相关产品推荐
相关产品推荐

