You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Windows启用LowLevelKeyboardProc钩子时SendInput无法发送键盘输入问题

问题根因

代码存在3个核心逻辑错误,导致钩子生效时SendInput无法正常工作:

  • 钩子回调分支缺失兜底传递逻辑:仅在kbdStruct.flags != LLKHF_INJECTED的分支内调用了CallNextHookEx,自身SendInput产生的带LLKHF_INJECTED标记的注入按键事件,走到回调后没有被传递给钩子链下一环,直接被丢弃。
  • 消息循环被阻塞:把sendString直接放在GetMessageW循环内,且每个字符发送前加250ms Sleep,直接卡死了低级键盘钩子依赖的消息泵。低级键盘钩子的回调运行在安装钩子的线程中,消息泵阻塞时,不仅钩子会失效,SendInput注入的事件也无法被系统正常派发。
  • 按键拦截不完整:仅拦截了按键按下事件(WM_KEYDOWN/WM_SYSKEYDOWN),没有拦截对应的抬起事件,真实物理按键的抬起事件仍会传递到前台窗口,会造成全局按键状态错乱。
修复方案

按以下规则调整代码即可正常运行:

  1. 修正钩子回调逻辑:所有不需要拦截的事件(包括自身注入的按键事件),必须调用CallNextHookEx传递给钩子链,仅对需要屏蔽的真实物理按键(无LLKHF_INJECTED标记)返回非0值吞掉事件。
  2. 保证消息循环畅通:不要在消息循环、钩子回调中加入长耗时Sleep、阻塞操作,消息循环仅负责正常分发消息,字符串发送逻辑通过定时器或者其他异步方式触发,不要阻塞消息泵。
  3. 补全按键拦截范围:同时拦截真实物理按键的按下、抬起事件,避免按键状态异常。

修复后的完整可运行代码:

#include <Windows.h>
#include <vector>
#include <string>

HHOOK kHook;

LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) {
    if (nCode == HC_ACTION)
    {
        KBDLLHOOKSTRUCT* kbdStruct = (KBDLLHOOKSTRUCT*)lParam;
        // 非注入事件为真实物理按键,全部拦截
        if (!(kbdStruct->flags & LLKHF_INJECTED))
        {
            switch (wParam)
            {
                case WM_KEYDOWN:
                case WM_SYSKEYDOWN:
                case WM_KEYUP:
                case WM_SYSKEYUP:
                    return 1;
            }
        }
    }
    // 其余事件(含自身SendInput注入的事件)正常传递
    return CallNextHookEx(kHook, nCode, wParam, lParam);
}

void sendString(const std::wstring& message)
{
    std::vector<INPUT> inputList;
    for (auto ch : message)
    {
        INPUT keyDown = {0};
        keyDown.type = INPUT_KEYBOARD;
        keyDown.ki.dwFlags = KEYEVENTF_UNICODE;
        keyDown.ki.wScan = ch;
        inputList.push_back(keyDown);

        INPUT keyUp = {0};
        keyUp.type = INPUT_KEYBOARD;
        keyUp.ki.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP;
        keyUp.ki.wScan = ch;
        inputList.push_back(keyUp);
    }
    SendInput(inputList.size(), inputList.data(), sizeof(INPUT));
}

int main()
{
    kHook = SetWindowsHookExW(WH_KEYBOARD_LL, LowLevelKeyboardProc, GetModuleHandleW(NULL), 0);
    MSG msg;
    // 示例:安装钩子3秒后发送测试字符串,不阻塞消息循环
    SetTimer(NULL, 0, 3000, [](HWND, UINT, UINT_PTR, DWORD) {
        sendString(L"Some String");
    });
    while (GetMessageW(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessageW(&msg);
    }
    UnhookWindowsHookEx(kHook);
    return 0;
}

注意:WH_KEYBOARD_LL属于线程级全局钩子,不需要封装到DLL中,但系统对钩子响应时间有严格限制,如果安装钩子的线程消息泵阻塞超过一定时间,系统会自动绕过该钩子,直接传递键盘事件,因此必须保证该线程的消息循环随时处于可响应状态。

内容的提问来源于stack exchange,提问作者LC0

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 00:12:29