Why are Low Level Mouse Hooks Causing Lag

661 Views Asked by At

I'm trying to make an application that just detects if the left mouse button is held down. In trying to do this, as well as learn mouse hooks, I copy-pasted a hook from an example source (https://cboard.cprogramming.com/windows-programming/119909-setwindowshookex-lowlevelmouseproc.html) just to see what it would do. The problem is that it lags my computer. Why is this, and how can I fix it?

#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <iostream>
#include <cstdio>
using namespace std;

HHOOK g_Hook;
HANDLE g_evExit;

LRESULT CALLBACK LowLevelMouseProc (int code, WPARAM wParam, LPARAM lParam)
{
    if (code == HC_ACTION)
    {
        const char *msg;
        char msg_buff[128];
        switch (wParam)
        {
            case WM_LBUTTONDOWN: msg = "WM_LBUTTONDOWN"; break;
            case WM_LBUTTONUP: msg = "WM_LBUTTONUP"; break;
            case WM_RBUTTONDOWN: msg = "WM_RBUTTONDOWN"; break;
            case WM_RBUTTONUP: msg = "WM_RBUTTONUP"; break;
            default: 
                sprintf(msg_buff, "Unknown msg: %u", wParam); 
                msg = msg_buff;
                break;
        }//switch

        const MSLLHOOKSTRUCT *p = 
            reinterpret_cast<const MSLLHOOKSTRUCT*>(lParam);
        cout << msg << " - [" << p->pt.x << ',' << p->pt.y << ']' << endl;

        static bool left_down = false;
        static bool right_down = false;
        switch (wParam)
        {
            case WM_LBUTTONDOWN: left_down = true; break;
            case WM_LBUTTONUP:   left_down = false; break;
            case WM_RBUTTONDOWN: right_down = true; break;
            case WM_RBUTTONUP:   right_down = false; break;
        }//switch

        if (left_down && right_down)
            SetEvent(g_evExit);
    }//if

    return CallNextHookEx(g_Hook, code, wParam, lParam); 
}//LowLevelMouseProc


int main()
{
    g_evExit = CreateEvent(0, TRUE, FALSE, 0);
    if (!g_evExit)
    {
        cerr << "CreateEvent failed, le = " << GetLastError() << endl;
        return 1;
    }//if

    g_Hook = SetWindowsHookEx(WH_MOUSE_LL, &LowLevelMouseProc, 
                              GetModuleHandle(0), 0);
    if (!g_Hook)
    {
        cerr << "SetWindowsHookEx() failed, le = " << GetLastError() << endl;
        return 1;
    }//if

    cout << "Press both left and right mouse buttons to exit..." << endl;

    MSG msg;
    DWORD status;
    while (1)
    {
        while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
            DispatchMessage(&msg);

        status = MsgWaitForMultipleObjects(1, &g_evExit, FALSE, 
                                           INFINITE, QS_ALLINPUT);
        if (status == (WAIT_OBJECT_0 + 1))
        {
            // there are messages to process, eat em up
            continue;
        }//if
        else
        {
            // assume g_evExit is signaled
            break;
        }//else
    }//while

    cout << "Exiting..." << endl;
    UnhookWindowsHookEx(g_Hook);
    CloseHandle(g_evExit);
    return 0;
}//main
1

There are 1 best solutions below

0
Lonami On

You need a message pump. The code you posted uses PeekMessage in combination with MsgWaitForMultipleObjects, but it's probably a better idea to use GetMessage along with TranslateMessage and DispatchMessage, as showcased in How to manually run message pump in C++:

MSG msg;
while (GetMessage(&msg, NULL, 0, 0) > 1) {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}

With this loop, I was able to set up a WH_MOUSE_LL hook with SetWindowsHookExA without having the mouse input lag, and correctly receiving all events.

Make sure the hook is installed on the same thread that will pump the messages.