1

我在带有 Visual Studio 2019 的 Windows7 上使用 Windows API。

我正在尝试设置一个低级键盘挂钩并读取控制台的键输入,为此我使用函数:HHOOK SetWindowsHookExA( [in] int idHook, [in] HOOKPROC lpfn, [in] HINSTANCE hmod, [in] DWORD dwThreadId ) 和参数:WH_KEYBOARD_LL所以我可以监视键盘事件。

这是我的主要功能:

int main()
{
    ...

    HHOOK keyBoardHook = SetWindowsHookExW(WH_KEYBOARD_LL, keyboardHook, NULL, 0);
    
    MSG message;

    while (true)
    {
        while(PeekMessage(&message, NULL, 0, 0, PM_REMOVE) != 0)
        {
            DispatchMessage(&message);
        }
    }

    ...

    UnhookWindowsHookEx(keyBoardHook);

    return 0;
}

这是回调实现:

LRESULT CALLBACK keyboardHook(int nCode, WPARAM wParam, LPARAM lParam)
{
    if (nCode == HC_ACTION)
    {
        KBDLLHOOKSTRUCT* keyData = (KBDLLHOOKSTRUCT*)lParam;

        if (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN)
        {
            fileHandler.addStr(keyHandler.translate(keyData,true,topWindowThreadId));
        }
        else if (wParam == WM_KEYUP || wParam == WM_SYSKEYUP)
        {
            fileHandler.addStr(keyHandler.translate(keyData,false,topWindowThreadId));
        }
    }
    return CallNextHookEx(NULL, nCode, wParam, lParam);
}

这两个变量如何工作不是这里的问题,而只是知道在回调中keyHandler翻译消息并将fileHandler结果打印到文件中。

问题来了。回调接收:

  • 当我按下一个键时的按键事件

  • 我释放时的按键事件

  • 当我按住按键时重复按键事件

  • VK_LCONTROL焦点窗口更改时的控制键向上事件

为什么会这样?据我所知,MSDN 文档中从未提及此行为。如果我从控制台窗口或任何窗口切换到任何其他窗口,则会生成此事件[left control up]并由我的keyHandler!

现在为了确保我的代码没有问题,我将回调更改为:

LRESULT CALLBACK keyboardHook(int nCode, WPARAM wParam, LPARAM lParam)
{
    if (nCode == HC_ACTION)
    {
        KBDLLHOOKSTRUCT* keyData = (KBDLLHOOKSTRUCT*)lParam;
        
        if (keyData->vkCode == VK_LCONTROL)
        {
            printf("CTRL");
        }
    }
    return CallNextHookEx(NULL, nCode, wParam, lParam);
}

并且每当我更改焦点窗口时仍然CTRL会打印到控制台,请注意仅VK_LCONTROL生成并且 wParam 始终WM_KEYUP

这不应该发生!

我尝试分析和修改我的代码,尝试从主函数中删除所有内容,直到它看起来就像上面的示例一样,甚至重新启动计算机以检查它是否是内部问题,没有任何帮助。当在网上搜索具有类似问题的类似帖子时,我发现完全没有,这使得它更加混乱。

更新

我尝试从 GitHub 编译别人的键盘挂钩相关代码,同样的问题.. 这正常吗?为什么没有记录?

4

1 回答 1

0

根据微软官方文档中对SetWindowsHookExA的描述。

全局挂钩是一种共享资源,安装一个挂钩会影响与调用线程相同的桌面中的所有应用程序。所有全局挂钩函数都必须在库中。全局挂钩应仅限于特殊用途的应用程序或在应用程序调试期间用作开发辅助。不再需要钩子的库应删除其钩子过程。

HHOOK SetWindowsHookExA(

[in] int idHook,

[在] HOOKPROC lpfn,

[在] HINSTANCE hmod,

[在] DWORD dwThreadId

);

对于桌面应用程序,如果此参数 (dwThreadId) 为零,则挂钩过程与在与调用线程相同的桌面中运行的所有现有线程相关联。这就是为什么在您更改焦点窗口后,CTRL 仍会打印到控制台。所以建议在DLL中使用hook。

于 2022-01-13T07:38:53.320 回答