Qt实现全局鼠标事件监听器-Windows版🍇
文章目录
- Qt实现全局鼠标事件监听器-Windows版🍇
- 1、概述🍈
- 2、实现效果🍉
- 3、实现方式🍊
- 4、关键代码🍋
- 5、源代码🍌
 
| 更多精彩内容 | 
|---|
| 👉个人内容分类汇总 👈 | 
| 👉Qt自定义模块、工具👈 | 
1、概述🍈
- Qt版本:V5.12.5
- 兼容系统: 
  - Windows:这里测试了Windows10,其它的版本没有测试;
- Linux:这里测试了ubuntu18.04、20.04,其它的没有测试(ubuntu自带的截图软件没有这个功能);
- Mac:等啥时候我有了Mac电脑再说。
 
- 有时候我们想获取到【系统全局鼠标事件】,使用Qt的鼠标事件、事件过滤器之类的都无法实现,因为当鼠标移出当前窗口或者当前窗口失去焦点、窗口最小化了就无法获取到鼠标事件了;
- 而Windows下想要监听到全局鼠标事件就需要使用到Windows的低级鼠标钩子来实现;
- 关于Windows的鼠标钩子API文档可以看微软官网SetWindowsHookExW ;
- 在这个类中通过Windows鼠标钩子API监听到全局鼠标事件;
- 然后将监听到的鼠标事件映射为QMouseEvent事件,便于在Qt里面使用。
2、实现效果🍉

3、实现方式🍊
- 使用
SetWindowsHookExW()函数挂钩低级鼠标钩子;- 通过回调函数
LowLevelMouseProc(int nCode, WPARAM wParam, LPARAM lParam)监听到全局鼠标事件;- wParam参数表示信号类型:
- WM_LBUTTONDOWN:鼠标左键按下
- WM_LBUTTONUP:鼠标左键抬起
- WM_MOUSEMOVE:鼠标移动
- WM_MOUSEWHEEL:鼠标滚轮
- WM_MOUSEHWHEEL:鼠标的水平滚轮倾斜或旋转(一般没用)
- WM_RBUTTONDOWN :鼠标右键按下
- WM_RBUTTONUP:鼠标右键抬起
- 使用
MSLLHOOKSTRUCT * msll = reinterpret_cast<MSLLHOOKSTRUCT*>(lParam);将lParam转换为MSLLHOOKSTRUCT结构体的指针,可通过这个结构体获取当前鼠标的坐标或者鼠标滚轮向前还是向后滚动的值。- 然后将获取到的鼠标事件映射为QMouseEvent、QWheelEvent事件,发送给当前程序使用;
- 这里我使用的是QMouseEvent、QWheelEvent指针进行发送,由于QMouseEvent、QWheelEvent没有默认无参构造,所以在Linux下不支持使用信号发送QMouseEvent、QWheelEvent变量,所以只能使用指针;
- 因为传递的是指针,所以在接收信号的槽函数里使用完后需要Delete,避免内存泄漏;
- 简易这个信号只绑定一次,避免多个槽函数里使用同一个指针,一个槽函数释放了另外一个槽函数里出现野指针或者重复释放。
- 不使用时需要使用
UnhookWindowsHookEx()函数删除SetWindowsHookEx ()函数在挂钩链中安装的挂钩过程。
4、关键代码🍋
- 由于使用到了系统API,所以pro文件中需要链接系统库
win32 {
LIBS+= -luser32    # 使用WindowsAPI需要链接库
}
- globalmouseevent.h
/******************************************************************************
 * @文件名     mouseevent.h
 * @功能       全局鼠标事件监听类
 *
 * @开发者     mhf
 * @邮箱       1603291350@qq.com
 * @时间       2022/12/07
 * @备注
 *****************************************************************************/
#ifndef MOUSEEVENT_H
#define MOUSEEVENT_H
#include <QObject>
class QMouseEvent;
class QWheelEvent;
/**
 *  全局鼠标事件单例信号类
 */
class GlobalMouseEvent : public QObject
{
    Q_OBJECT
public:
    static GlobalMouseEvent* getInstance()
    {
        static GlobalMouseEvent mouseEvent;
        return &mouseEvent;
    }
    static bool installMouseEvent();      // 安装全局鼠标事件监听器
    static bool removeMouseEvent();       // 卸载全局鼠标事件监听器
signals:
    /**
     * @brief 由于传递的是指针,为了保证不会出现内存泄露,需要在槽函数中delete。
     *        建议此信号只绑定一次,因为如果绑定多次可能会出现一个槽函数里把信号delete了,另外一个槽函数还在使用,出现野指针,或者多个槽函数多次delete
     */
    void mouseEvent(QMouseEvent* event);
    void wheelEvent(QWheelEvent* event);
private:
    GlobalMouseEvent(){}
};
#endif // MOUSEEVENT_H
- globalmouseevent_win.cpp
#include "globalmouseevent.h"
#if defined(Q_OS_WIN)
#include <QDebug>
#include <QCursor>
#include <QMouseEvent>
#include "Windows.h"
static HHOOK g_hook = nullptr;
/**
 * @brief           处理鼠标事件的回调函数,由于这不是一个成员函数,所以需要通过中间单例类mouseEvent将鼠标信号传递出来
 *                  具体内容看https://learn.microsoft.com/zh-cn/previous-versions/windows/desktop/legacy/ms644986(v=vs.85)
 * @param nCode     挂钩过程用于确定如何处理消息的代码。如果nCode小于零,则挂钩过程必须将消息传递给 CallNextHookEx 函数而不进行进一步处理,并且应返回CallNextHookEx返回的值
 * @param wParam    信号类型:WM_LBUTTONDOWN、WM_LBUTTONUP、WM_MOUSEMOVE、WM_MOUSEWHEEL、WM_MOUSEHWHEEL、WM_RBUTTONDOWN 或WM_RBUTTONUP(鼠标中键点击和拓展按还没找到怎么弄)。
 * @param lParam    MSLLHOOKSTRUCT结构体指针
 * @return
 */
LRESULT CALLBACK LowLevelMouseProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    QPoint point = QCursor::pos();  // 获取鼠标当前位置
    switch (wParam)
    {
    case WM_LBUTTONDOWN:   // 鼠标左键按下
        emit GlobalMouseEvent::getInstance()->mouseEvent(new QMouseEvent(QEvent::MouseButtonPress, point, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier));
        break;
    case WM_MOUSEMOVE:     // 鼠标移动
        emit GlobalMouseEvent::getInstance()->mouseEvent(new QMouseEvent(QEvent::MouseMove, point, Qt::NoButton, Qt::NoButton, Qt::NoModifier));
        break;
    case WM_RBUTTONDOWN:   // 鼠标右键按下
        emit GlobalMouseEvent::getInstance()->mouseEvent(new QMouseEvent(QEvent::MouseButtonPress, point, Qt::RightButton, Qt::RightButton, Qt::NoModifier));
        break;
    case WM_RBUTTONUP:     // 鼠标右键抬起
        emit GlobalMouseEvent::getInstance()->mouseEvent(new QMouseEvent(QEvent::MouseButtonRelease, point, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier));
        break;
    case WM_LBUTTONUP:     // 鼠标左键抬起
        emit GlobalMouseEvent::getInstance()->mouseEvent(new QMouseEvent(QEvent::MouseButtonRelease, point, Qt::RightButton, Qt::RightButton, Qt::NoModifier));
        break;
    case WM_MOUSEWHEEL:    // 鼠标滚轮
    {
        MSLLHOOKSTRUCT * msll = reinterpret_cast<MSLLHOOKSTRUCT*>(lParam);
//        qDebug() << QString("坐标:(%1, %2)").arg(msll->pt.x).arg(msll->pt.y);     // 获取鼠标坐标
        int delta = GET_WHEEL_DELTA_WPARAM(msll->mouseData);                     // 获取滚轮状态,向前:120,向后-120
        emit GlobalMouseEvent::getInstance()->wheelEvent(new QWheelEvent(point, delta, Qt::MiddleButton, Qt::NoModifier));
        break;
    }
    default:
        break;
    }
    return CallNextHookEx(nullptr, nCode, wParam, lParam);   // 注意这一行一定不能少,否则会出大问题
}
/**
 * @brief  安装全局鼠标事件监听器
 * @return
 */
bool GlobalMouseEvent::installMouseEvent()
{
    if(g_hook) return true;     // 避免重复安装
    /**
     * WH_KEYBOARD_LL 为全局键盘钩子, WH_MOUSE_LL 为全局鼠标钩子
     * 详细说明看官方文档:https://learn.microsoft.com/zh-cn/windows/win32/api/winuser/nf-winuser-setwindowshookexw
     */
    g_hook = SetWindowsHookExW(WH_MOUSE_LL, LowLevelMouseProc, GetModuleHandleW(nullptr), 0);
    return g_hook;
}
/**
 * @brief   卸载全局鼠标事件监听器
 * @return
 */
bool GlobalMouseEvent::removeMouseEvent()
{
    if(!g_hook) return true;   // 避免重复卸载
    bool ret = UnhookWindowsHookEx(g_hook);
    if(ret)
    {
        g_hook = nullptr;
        return true;
    }
    return false;
}
#endif
5、源代码🍌
- gitee
- github
- 全局鼠标键盘事件监听器仓库github
- 全局鼠标键盘事件监听器仓库gitee
- CSDN
- 可以使用命令git clone https://gitee.com/mahuifa/QtGlobalEvent.git直接下载仓库,然后引用到自己的程序中。
🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒



















