【问题标题】:SetWindowsHookEx stops receiving WH_MOUSE_LL events when window focus lost当窗口焦点丢失时,SetWindowsHookEx 停止接收 WH_MOUSE_LL 事件
【发布时间】:2021-02-03 19:33:27
【问题描述】:

我有一个打开 SDL 窗口的 SDL2 程序。在main 中,我为鼠标事件创建了一个钩子

hMSHook = SetWindowsHookEx(WH_MOUSE_LL, MouseProc, NULL, NULL);

main 之外我有自定义鼠标事件处理程序

HHOOK hMSHook;
int xPosAbsolute = 0;
int yPosAbsolute = 0;

LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    if (nCode == HC_ACTION)
    {
        auto &ms = *(const MOUSEHOOKSTRUCT *)lParam;
        if (wParam == WM_MOUSEMOVE)
        {
            xPosAbsolute = ms.pt.x;
            yPosAbsolute = ms.pt.y;
        }
    }
    return CallNextHookEx(hMSHook, nCode, wParam, lParam);
}

当窗口处于焦点时,鼠标坐标xPosAbsoluteyPosAbsolute 会随着鼠标移动而正确更新。但是,如果另一个窗口获得焦点,这些变量将停止更新。

我的问题是,我如何让SetWindowsHookEx() 在我的窗口未聚焦时继续接收WH_MOUSE_LL 事件?

编辑:

实际上,事实证明,SetWindowsHookEx 仅在窗口焦点丢失到另一个以管理员身份启动的窗口时才停止接收事件更新,而该程序的 SDL2 窗口以普通用户身份启动。一旦当前程序也以管理员身份启动,所有更新都会继续进来(使用上面的代码),即使窗口焦点丢失。所以结果证明这不是问题。

【问题讨论】:

  • 对于低级挂钩,SetWindowsHookEx()hMod 参数应该为 NULL,而不是 hInstanceSetWindowsHookEx() 文档甚至指出:“hMod 参数必须设置为 NULL [...] 如果挂钩过程在与当前进程关联的代码内。
  • 话虽如此,请考虑使用Raw Input API 而不是SetWindowsHookEx()LowLevelMouseProc() 文档甚至指出:“在应用程序需要使用低级挂钩的大多数情况下,它应该监控原始输入。
  • @RemyLebeau 感谢您指出这一点。我在我的代码和上面的问题中解决了这个问题。不幸的是,当窗口焦点丢失时SetWindowsHookEx() 未接收事件的问题仍然存在。
  • 安装钩子的线程是否有消息循环?低级别的钩子事件在安装线程中运行(这就是为什么 Raw Input 在钩子其他应用时效率更高的原因),因此线程需要有消息处理。
  • @RemyLebeau 它没有,因为GetMessage 是阻塞的,而有一个循环来执行其他功能。我不确定如何将两者合并...不幸的是,我对原始输入不够熟悉,无法让它做同样的事情。

标签: c++ winapi hook


【解决方案1】:

不需要专注,但需要持续发送消息。

以下是一个带有低级挂钩的迷你 Win32 窗口应用程序,您可以参考。无论哪个窗口拥有焦点,它都会在与调用线程在同一桌面上运行的所有现有线程中接收 WM_MOUSEMOVE 消息。

LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    if (nCode == HC_ACTION)
    {
        auto& ms = *(const MOUSEHOOKSTRUCT*)lParam;
        if (wParam == WM_MOUSEMOVE)
        {
            OutputDebugString(L"\n WM_MOUSEMOVE \n");
            OutputDebugStringA(to_string(counter++).c_str());
        }
    }
    return CallNextHookEx(NULL, nCode, wParam, lParam);
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    return DefWindowProc(hWnd, message, wParam, lParam);
}

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    HHOOK hMSHook = SetWindowsHookEx(WH_MOUSE_LL, MouseProc, NULL, NULL);

    WNDCLASSEXW wcex = {0};
    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.lpfnWndProc = WndProc;
    wcex.hInstance = hInstance;
    wcex.lpszClassName = szWindowClass;
    RegisterClassExW(&wcex);

    HWND hWnd = CreateWindowW(szWindowClass, L"test", WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    MSG msg;

    // Main message loop:
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return (int) msg.wParam;
}

【讨论】:

  • 我看到这段代码创建了一个新窗口并且有一个阻塞的主消息循环。在我的例子中,我使用 SDL2 创建一个窗口,并且为了使用 SDL 功能,我使用了不同的滚动周期更新循环。有没有办法为现有窗口设置属性以在焦点丢失后继续接收消息?您的哪一行代码专门确保了该属性?
  • @Kagaratsch 由于GetMessage被阻塞,如果窗口失去焦点时GetMessage()不能连续执行,如果是这样,我们需要找出原因@987654323 @块。
猜你喜欢
  • 2018-03-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-25
  • 2011-03-07
  • 2021-08-22
  • 1970-01-01
相关资源
最近更新 更多