WinAPI - 钩住鼠标

Posted

技术标签:

【中文标题】WinAPI - 钩住鼠标【英文标题】:WinAPI - hooking mouse 【发布时间】:2020-02-21 22:24:01 【问题描述】:

我在尝试挂​​钩鼠标事件时遇到问题。如果我在我的应用程序窗口之外单击它会起作用。但是当我在我的应用程序窗口内单击时,它不会检测到 WM_LBUTTONUP 事件。几秒钟的鼠标移动非常缓慢。并且从那时起它不会检测到任何事件。即使我在我的应用窗口之外点击。

所以我的应用程序无限期地运行。我想知道这是否与线程或其他东西有关。 也许如果我想在我的计算机的所有线程中全局跟踪某些东西(例如跟踪鼠标移动),那么我可以在我的应用线程中什么都不做? 但这对我来说很奇怪。

这是鼠标钩子的代码。 main.cpp

#include <windows.h>
#include <iostream>
#include "MyHook.h"

using namespace std;

int main()

    MyHook::Instance().InstallHook();
    return MyHook::Instance().Messsages();

MyHook.h

#pragma once
#include <Windows.h>

class MyHook

public:
    //single ton
    static MyHook& Instance()
    
        static MyHook myHook;
        return myHook;
    

    // function to install our mouseHook
    void InstallHook();

    // function to uninstall our mouseHook
    void UninstallHook();

    // function to "deal" with our messages
    int Messsages();

public:
    HHOOK mouseHook; // handle to the mouse hook
    HHOOK windowHook; // handle to the window hook
    MSG msg; // struct with information about all messages in our queue
;

LRESULT CALLBACK MyMouseCallback(int nCode, WPARAM wParam, LPARAM lParam);

MyHook.cpp

#include "MyHook.h"

#include <stdio.h>

void MyHook::InstallHook()

    /*
    SetWindowHookEx(
    WM_MOUSE_LL = mouse low level mouseHook type,
    MyMouseCallback = our callback function that will deal with system messages about mouse
    NULL, 0);

    c++ note: we can check the return SetWindowsHookEx like this because:
    If it return NULL, a NULL value is 0 and 0 is false.
    */
    if (!(mouseHook = SetWindowsHookEx(WH_MOUSE_LL, MyMouseCallback, NULL, 0)))
    
        printf_s("Error: %x \n", GetLastError());
    


// function to uninstall our mouseHook
void MyHook::UninstallHook()

    UnhookWindowsHookEx(mouseHook);


MSG msg; // struct with information about all messages in our queue

// function to "deal" with our messages
int MyHook::Messsages()

    // while we do not close our application
    while (msg.message != WM_QUIT)
    
        if (GetMessage(&msg, NULL, 0, 0/*, PM_REMOVE*/))
        
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        
        Sleep(1);
    
    UninstallHook(); // if we close, let's uninstall our mouseHook
    return (int)msg.wParam; // return the messages


LRESULT CALLBACK MyMouseCallback(int nCode, WPARAM wParam, LPARAM lParam)

    MSLLHOOKSTRUCT* pMouseStruct = (MSLLHOOKSTRUCT*)lParam; // WH_MOUSE_LL struct
    /*
    nCode, this parameters will determine how to process a message
    This callback in this case only have information when it is 0 (HC_ACTION): wParam and lParam contain info

    wParam is about WINDOWS MESSAGE, in this case MOUSE messages.
    lParam is information contained in the structure MSLLHOOKSTRUCT
    */

    // we have information in wParam/lParam ? If yes, let's check it:
    if (nCode == 0)
     
        // Mouse struct contain information?    
        // if (pMouseStruct != NULL)
        // 
        //  printf_s("Mouse Coordinates: x = %i | y = %i \n", pMouseStruct->pt.x, pMouseStruct->pt.y);
        // 

        switch (wParam)
        
            case WM_LBUTTONDOWN:
            
                printf_s("LEFT CLICK DOWN\n");
            
            break;
            case WM_LBUTTONUP:
            
                printf_s("LEFT CLICK UP\n");
            
            break;


        

    

    /*
    Every time that the nCode is less than 0 we need to CallNextHookEx:
    -> Pass to the next mouseHook
         MSDN: Calling CallNextHookEx is optional, but it is highly recommended;
         otherwise, other applications that have installed hooks will not receive mouseHook notifications and may behave incorrectly as a result.
    */
    return CallNextHookEx(MyHook::Instance().mouseHook, nCode, wParam, lParam);

【问题讨论】:

"但我的应用每次都死机" - *** 不是调试服务。您是否尝试过使用调试器单步执行代码以查看实际发生冻结的确切位置? 是的,我在遇到问题时会使用调试器。但在这种情况下,我不知道它有什么用处。我几乎没有变数。我只有一个隐式循环。对于所有活动窗口,我显示坐标并尝试使用 SetWindowPos() 更改窗口的位置。我知道显示坐标不是问题。所以 SetWindowPos() 有一些东西。 "我不知道它有什么用处" - 它可以让你找到真正冻结的 exact 代码行.你认为SetWindowPos(),但你验证了吗?也许是std::cin.ignore()?谁知道。不要做假设。哦,顺便说一句,您的 enumWindowCallback() 没有进行足够的错误处理,并且正在泄漏内存。 我刚才使用了调试器,是的,问题出在 SetWindowPos() 上。但我不知道为什么。我修复了代码,现在我释放内存。但是还是不行。 请不要修改问题。最初的问题是关于枚举窗口并重新定位它们。新问题是关于鼠标钩子的。与旧问题相关的 cmets 不再有意义。如果您有新问题,请提出新问题。 【参考方案1】:

我没有运行你的代码,所以我不确定,但我有一个想法。

printf_s 进行控制台输出。单击您的控制台窗口可以通过选择/复制您的输出和阻止printf_s 之类的功能来完成这个花哨的事情,以避免选择逃跑。由于您的挂钩功能被阻止,因此鼠标被阻止。稍后在 Windows 上可能能够删除您的钩子,因为这种钩子程序运行时间过长。

改为记录到文件,或将日志发布到队列并从该队列打印。

【讨论】:

以上是关于WinAPI - 钩住鼠标的主要内容,如果未能解决你的问题,请参考以下文章

WinAPI 鼠标点击无法正常工作

水平鼠标滚轮事件? (WinAPI)

如何找出winapi的主要鼠标按钮?

Delphi - 通过WinAPI GetCursorPos实现鼠标位置的实时显示

如何使用 WinAPI 检测控制台中的符号?

Winapi检测按钮悬停