SetWindowsHookExW 不调用 MouseProc 回调过程

SetWindowsHookExW not calling the MouseProc callback procedure

提问人:vibhav950 提问时间:10/14/2023 最后编辑:vibhav950 更新时间:10/20/2023 访问量:179

问:

因此,我正在尝试用 C 语言运行一个简单的代码,该代码在每次移动时打印鼠标的位置。为了让程序运行一段时间然后退出,我使用了如下所示的 for 循环。我希望钩子仅与当前线程相关联。 代码如下:

#include <stdio.h>
#include <windows.h>

// Global hook handle
HHOOK hHook = NULL;

// Mouse hook callback
LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam) {
    printf("Entered\n");

    if (nCode >= 0) {
        if ((nCode == HC_ACTION) && (wParam == WM_MOUSEMOVE)) {
            MOUSEHOOKSTRUCT* pMouseStruct = (MOUSEHOOKSTRUCT*)lParam;
            printf("Mouse X: %ld, Mouse Y: %ld\n", pMouseStruct->pt.x, pMouseStruct->pt.y);
        }
    }

    return CallNextHookEx(hHook, nCode, wParam, lParam);
}

int main() {
    // Install the mouse hook
    hHook = SetWindowsHookEx(WH_MOUSE, (HOOKPROC) &MouseHookProc, NULL, GetCurrentThreadId());

    if (hHook == NULL) {
        printf("Hook installation failed\n");
        return 1;
    }

    // Keep program running (edit 1)
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0) > 0) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    // Unhook and exit
    UnhookWindowsHookEx(hHook);
    return 0;
}

printf 语句永远不会被执行。为什么即使成功设置了钩子,进程也没有被调用?

编辑:我很抱歉没有明确提及这一点,但我最初尝试使用消息循环而不是上面显示的循环,但结果是一样的。但是,如果我改用低级鼠标钩子,它确实有效。为什么会这样?forWH_MOUSE_LL

更新:我会试着描述我想要实现的更好目标。首先,我正在尝试制作一个独立的控制台可执行文件,而不是 Windows 应用程序。我最初尝试轮询鼠标位置的次数有限,但现在我相信对于我的应用程序来说,更好的方法是只要程序处于活动状态,就进行采样,然后释放钩子以及其他清理作业以优雅地退出程序。我面临的问题是我无法使用普通的鼠标钩而不是低级的鼠标钩。我从一些来源读到,钩子过程必须通过DLL注入到可执行文件中,但我也无法让它工作。现在唯一有效的是一个低级鼠标钩子,它减慢了我的程序速度并使其几乎无法使用。WM_MOUSEWM_MOUSE_LL

c winapi 鼠标钩

评论

0赞 RbMm 10/14/2023
您没有消息循环。在 GetMessage/PeekMessage 中调用的钩子。但你不调用这个 API。结果和钩子将不会被调用
2赞 IInspectable 10/14/2023
这在文档中明确说明:“这个钩子可以在安装它的线程的上下文中调用。通过向安装挂钩的线程发送消息来进行调用。因此,安装钩子的线程必须具有消息循环。
0赞 Remy Lebeau 10/15/2023
@IInspectable应该作为答案而不是评论发布
0赞 IInspectable 10/15/2023
@RemyLebeau 作为答案,这有什么用呢?这是一个完全疯狂的问题。当他们感兴趣的线程永远不会触发这些钩子事件时,告诉他们如何接收钩子回调是没有用的。
0赞 vibhav950 10/16/2023
即使使用消息循环,它也不起作用。它确实适用于低级鼠标钩子,但如果我尝试使用普通鼠标钩子将钩子与当前线程相关联,则不行。WH_MOUSE_LL

答:

2赞 Júlio César Schincariol Filho 10/15/2023 #1

正如注释中指出的,您需要一个 Win32 消息循环才能接收挂钩事件。WH_MOUSE

https://learn.microsoft.com/en-us/windows/win32/winmsg/mouseproc

可以在安装它的线程的上下文中调用此钩子。通过向安装挂钩的线程发送消息来进行调用。因此,安装钩子的线程必须具有消息循环。

示例 (main.cpp):

#include <Windows.h>
#include <iostream>

static HHOOK p_hook = nullptr;

BOOL WINAPI HandlerRoutine(_In_ DWORD dwCtrlType);
LRESULT CALLBACK LowLevelMouseProc(_In_ int nCode, _In_ WPARAM wParam, _In_ LPARAM lParam);

int main()
{
    if (SetConsoleCtrlHandler(HandlerRoutine, TRUE) == 0)
        return ~0;

    p_hook = SetWindowsHookEx(WH_MOUSE_LL, LowLevelMouseProc, nullptr, 0);
    if (p_hook == nullptr)
        return ~0;

    MSG msg = { 0 };
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    if (p_hook != nullptr)
        UnhookWindowsHookEx(p_hook);

    return static_cast<int>(msg.wParam);
}

LRESULT CALLBACK LowLevelMouseProc(_In_ int nCode, _In_ WPARAM wParam, _In_ LPARAM lParam)
{
    if (nCode == HC_ACTION)
    {
        auto m_struct = reinterpret_cast<MSLLHOOKSTRUCT*>(lParam);
        std::cout << "x: " << m_struct->pt.x << " y: " << m_struct->pt.y << std::endl;
    }
    return CallNextHookEx(p_hook, nCode, wParam, lParam);
}

BOOL WINAPI HandlerRoutine(_In_ DWORD dwCtrlType)
{
    if (dwCtrlType == CTRL_CLOSE_EVENT || dwCtrlType == CTRL_SHUTDOWN_EVENT)
    {
        if (UnhookWindowsHookEx(p_hook) == TRUE)
            p_hook = nullptr;
    }
    return TRUE;
}

评论

2赞 Remy Lebeau 10/15/2023
请注意,OP 使用的是钩子,但您的示例使用的是钩子。不同的语义,但两者确实存在相同的消息循环要求。WH_MOUSEWH_MOUSE_LL
0赞 YangXiaoPo-MSFT 10/20/2023 #2

正如我在评论中所说,绝对可以正常工作。WH_MOUSE

使用“监视系统事件”文档示例。

enter image description here

我需要停止轮询并释放钩子一次(比如说 100)的不同位置已被记录下来。

使用 UnhookWindowsHookEx

评论

0赞 vibhav950 10/20/2023
参考这篇文章,似乎 MouseProc 回调必须在 dll 中,而消息循环和 SetWindowsHookEx 必须在 exe 中。
0赞 YangXiaoPo-MSFT 10/20/2023
为什么不参考 MouseProc帖子
0赞 IInspectable 10/20/2023
OP 正在编写控制台应用程序。您已通过将 从 切换到 来更改问题。因此,此贡献解决了与问题中的问题陈述截然不同的问题。强烈建议删除。/SUBSYSTEM:CONSOLE/SUBSYSTEM:WINDOWS
0赞 YangXiaoPo-MSFT 10/24/2023
@vibhav950第三个程序,如果你不喜欢全局钩子,那么注入第三个程序的DLL就是办法。有关示例,请参阅 stackoverflow.com/a/75680011/15511041