Search code examples
c++winapihookdrag

How to detect drag in a mouse hook procedure


I've successfully set up a mouse hook for a window, and I'd like to detect a drag operation when the left mouse button is clicked. I've tried using DragDetect, as shown below, but it never returns TRUE, and it never suppresses the subsequent mouse-up event (in this code, hwnd is the target window):

LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    if (nCode < 0)
    {
        return CallNextHookEx(NULL, nCode, wParam, lParam);
    }

    switch (wParam)
    {
        case WM_LBUTTONDOWN:
        {
            // Get the click point
            LPMOUSEHOOKSTRUCT pMouseHookStruct = reinterpret_cast<LPMOUSEHOOKSTRUCT>(lParam);
            CPoint ptClick = pMouseHookStruct->pt;

            // Drag detect
            BOOL fDrag = DragDetect(hwnd, ptClick);
            if (fDrag)
            {
                MessageBox(NULL, L"Drag", NULL, MB_OK);
            }
        break;
        }
    }
    return CallNextHookEx(NULL, nCode, wParam, lParam);
}

Does DragDetect not work within hook procedures, or am I just doing something wrong?


Solution

  • As comments pointed, DragDetect does not predict the future. But you can compare the POINT of WM_LBUTTONDOWN and WM_MOUSEMOVE in a click-time.

    Sample:

    #include <windows.h>
    #include <iostream>
    using namespace std;
    typedef BOOL(*SETHOOK)(HWND hWnd);
    typedef BOOL(*UNHOOK)();
    LRESULT CALLBACK WndProcFunc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
        switch (message)
        {
        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;
        }
        return DefWindowProc(hwnd, message, wParam, lParam);
    }
    int main(int argc, char* argv[])
    {
        WNDCLASS wc{};
        wc.style = CS_HREDRAW | CS_VREDRAW;
        wc.lpfnWndProc = WndProcFunc;
        wc.hInstance = GetModuleHandle(NULL);
        wc.lpszClassName = L"Class_Name";
        wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
        RegisterClass(&wc);
    
        HWND hWnd = CreateWindow(L"Class_Name", L"Test", WS_OVERLAPPEDWINDOW, 0, 0, 1000, 500, NULL, NULL, GetModuleHandle(NULL), NULL);
    
        SETHOOK SetHook;
        UNHOOK UnHook;
        HMODULE h = LoadLibraryA("PATH\\DLL4.dll");
        SetHook = (SETHOOK)GetProcAddress(h, "SetHook");
        UnHook = (UNHOOK)GetProcAddress(h, "UnHook");
        BOOL i = SetHook(hWnd);
    
        ShowWindow(hWnd, 1);
        UpdateWindow(hWnd);
    
        MSG Msg;
        while (GetMessage(&Msg, NULL, 0, 0))
        {
            TranslateMessage(&Msg);
            DispatchMessage(&Msg);
        }
    
        UnHook();
        return 0;
    }
    

    DLL:

    // dllmain.cpp : Defines the entry point for the DLL application.
    #include "pch.h"
    #include <windows.h>
    #include <stdio.h>
    #include <string.h>
    #include <math.h>
    #define PRESS TRUE
    #define RELEASE FALSE
    HHOOK tHook;
    HMODULE hinstDLL;
    BOOL button = RELEASE;
    BOOL Drag = FALSE;
    HWND hWnd;
    POINT pt = { 0 };
    LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam) {
        if (nCode < 0)
        {
            return CallNextHookEx(NULL, nCode, wParam, lParam);
        }
        LPMOUSEHOOKSTRUCT pMouseHookStruct = reinterpret_cast<LPMOUSEHOOKSTRUCT>(lParam);
        if (pMouseHookStruct->hwnd != hWnd)
        {
            button = RELEASE;
            Drag = FALSE;
        }
        else {
            switch (wParam)
            {
            case WM_LBUTTONDOWN:
            {
                button = PRESS;
                Drag = FALSE;
                printf("WM_LBUTTONDOWN\n");
                printf("%d , %d\n", pMouseHookStruct->pt.x, pMouseHookStruct->pt.y);
                pt.x = pMouseHookStruct->pt.x;
                pt.y = pMouseHookStruct->pt.y;
                break;
            }
            case WM_LBUTTONUP:
            {
                button = RELEASE;
                Drag = FALSE;
                printf("WM_LBUTTONUP\n");
                break;
            }
            case WM_MOUSEMOVE:
            {
                if (button == PRESS)
                {
                    printf("%d , %d\n", pMouseHookStruct->pt.x, pMouseHookStruct->pt.y);
                    if (Drag == FALSE && (abs(pt.x - pMouseHookStruct->pt.x) >= GetSystemMetrics(SM_CXDRAG) || abs(pt.y - pMouseHookStruct->pt.y) >= GetSystemMetrics(SM_CYDRAG)))
                    {
                        printf("Drag\n");
                        Drag = TRUE;
                    }
                }
                break;
            }
            }
        }
        return CallNextHookEx(NULL, nCode, wParam, lParam);
    }
    extern "C" __declspec(dllexport) BOOL SetHook(HWND hwnd)
    {
        hWnd = hwnd;
        tHook = SetWindowsHookEx(WH_MOUSE, MouseProc, hinstDLL, 0);
    
        if (tHook == NULL)
            return FALSE;
        else
            return TRUE;
    }
    extern "C" __declspec(dllexport) BOOL UnHook()
    {
        return UnhookWindowsHookEx(tHook);
    }
    
    
    BOOL APIENTRY DllMain( HMODULE hModule,
                           DWORD  ul_reason_for_call,
                           LPVOID lpReserved
                         )
    {
        switch (ul_reason_for_call)
        {
        case DLL_PROCESS_ATTACH:
            hinstDLL = hModule;
            break;
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
            break;
        }
        return TRUE;
    }