SetWindowsHookExW not calling the MouseProc callback procedure

240 Views Asked by At

So I'm trying to run a simple code in C that prints the position of the mouse every time it moves. To keep the program running for a while and then exit, I use a for loop shown below. I want the hook to be associated with the current thread only. Here's the code:

#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;
}

The printf statement never gets executed. Why is the proc not getting called even though the hook was set up successfully?

Edit: My apologies for not mentioning this explicitly but I had initially tried it with a message loop instead of the for loop shown above, but the result was the same. It does however, work if I use a low level mouse hook WH_MOUSE_LL instead. Why is this happening?

Update: I'll try to describe what I'm trying to achieve better. Firstly, I'm trying to make a standalone console executable, not a Windows app. I was initially trying to poll the mouse position a finite number of times but now I believe a better approach for my application would be to sample as long as the program is active, and then release the hooks along with with the other cleanup jobs to exit the program gracefully. The problem I'm facing is that I'm unable to use a normal mouse hook WM_MOUSE as opposed to a low-level one WM_MOUSE_LL. I read from some sources that the hook procedure must be injected into the executable through a DLL but I can't get that to work either. Right now the only thing that works is a low-level mouse hook which slows down my program and makes it almost unusable.

2

There are 2 best solutions below

4
On

As I said in the comment, WH_MOUSE absolutely definitely works fine.

Using the Monitoring System Events document sample.

enter image description here

I need to stop polling and release the hook once a certain number (say 100) of distinct positions have been recorded.

Use UnhookWindowsHookEx.

1
On

As pointed out in comments, you need a Win32 message loop to be able to receive WH_MOUSE hook events.

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

This hook may be called in the context of the thread that installed it. The call is made by sending a message to the thread that installed the hook. Therefore, the thread that installed the hook must have a message loop.

Sample (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;
}