qt鼠标键盘钩子类设计

说明

什么是钩子?

Windows系统程序的运行是建立在消息传递机制的基础之上的,几乎所有的程序活动都由消息来驱动。钩子机制可以看作是一个消息的中转站,控制系统发出消息的处理和传递。利用钩子,我们可以截获系统发给应用程序的消息,并且在经过处理后决定是否将消息再发给下一个应用程序。

在qt程序中,一般使用事件系统进行鼠标、按键输入的检测,但是这是有个前提条件的,就是需要聚焦在主界面或里面的对象中。

如果聚焦不在软件上面,其他地方点击鼠标或按下键盘则无法使用事件检测,这时候就需要用到Windows下的钩子技术,使用SetWindowsHookEx API接口设置钩子(回调函数),如下代码:

keyHook = SetWindowsHookEx(WH_KEYBOARD_LL,keyProc,nullptr,0);

keyProc为自定义的回调函数,用于按键检测和处理。使用UnhookWindowsHookEx接口解除钩子的绑定。如下:

UnhookWindowsHookEx(keyHook);

实现思路

上面说了,主体是使用SetWindowsHookEx API接口注册钩子函数。

除此之外,为了我们的软件更加方便使用,我们可以对其进行一层封装,将按键、键盘的检测封装在MyHook类中,使用单例模式实例化,并检测到全局的按键或按钮后,以信号的方式对外发出。

其中有的可能只要实现鼠标钩子就行,不需要按键检测,这时候我们就可以添加一个模式设置,模式类型如下:

 enum Model
    {
        eNull = 0,
        eKey = 1,
        eMouse = 2,
        eMouseKey = 3
    }mModel;

按模式注册钩子,不需要的就不注册,可以减少没必要的开销。

类代码

h文件

#ifndef MYHOOK_H
#define MYHOOK_H

#include <windows.h>
#include <winuser.h>
#include <QObject>

class MyHook:public QObject
{
    Q_OBJECT
public:
    enum Model
    {
        eNull = 0,
        eKey = 1,
        eMouse = 2,
        eMouseKey = 3
    }mModel;
    static MyHook* instance();
    void installHook(Model model);   //安装钩子函数,指针参数为了发送信号用
    void unInstallHook(Model model);        //删除钩子

private:
    MyHook();
};

#endif // MYHOOK_H

cpp文件

#include "myhook.h"
#include <QDebug>

static HHOOK keyHook = nullptr,mouseHook = nullptr;     //钩子对象
static MyHook *hookClass = nullptr;  //与钩子绑定的对象的指针

//钩子处理函数,包含键盘和鼠标一体
LRESULT CALLBACK keyProc(int nCode,WPARAM wParam,LPARAM lParam) //钩子消息函数,系统消息队列信息会返回到该函数中
{
    if(!hookClass)
        return CallNextHookEx(keyHook,nCode,wParam,lParam);

    if(wParam == WM_KEYDOWN)    //wParam用于判断事件类型,当前为按键按下事件
    {
        KBDLLHOOKSTRUCT* pkbhs = (KBDLLHOOKSTRUCT*)lParam;  //lParam用于判断按键类型
        if(pkbhs->vkCode == 0x31 && GetAsyncKeyState(VK_CONTROL))   //组合按键检测 按下Ctrl+1,哪个后按下的,使用GetAsyncKeyState函数判断
        {
            //处理程序
        }
        if(pkbhs->vkCode == 0x31)   //检测按键1按下
        {
            //处理程序
        }
        qDebug()<<"key enter:"<<QString::number(pkbhs->vkCode,16);
        //可添加一些自定义信号发出去,如:emit hookClass->keyPress
    }
    return CallNextHookEx(keyHook,nCode,wParam,lParam);     //继续原有的事件队列
}

//钩子处理函数,
LRESULT CALLBACK mouseProc(int nCode,WPARAM wParam,LPARAM lParam) //钩子消息函数,系统消息队列信息会返回到该函数中
{
    if(!hookClass)
        return CallNextHookEx(mouseHook,nCode,wParam,lParam);
    //鼠标点击右键
    if (WM_RBUTTONDOWN == wParam || WM_LBUTTONDOWN == wParam)
    {
        MOUSEHOOKSTRUCT *mhookstruct = (MOUSEHOOKSTRUCT*)lParam; //鼠标HOOK结构体
        POINT pt = mhookstruct->pt;         //将当前鼠标坐标点的x,y坐标作为参数向主程序窗口发送消息
        qDebug()<<QString("mouse enter:(%1,%2)").arg(pt.x).arg(pt.y);
        //可添加一些自定义信号发出去,如:emit hookClass->mousePress
    }
    return CallNextHookEx(mouseHook,nCode,wParam,lParam);     //继续原有的事件队列
}

MyHook::MyHook()
    :mModel(eNull)
{

}

MyHook *MyHook::instance()
{
    if(!hookClass)
        hookClass = new MyHook();
    return hookClass;
}

void MyHook::installHook(Model model) //安装钩子函数
{
    mModel = model;
    if(mModel | eKey)
        keyHook = SetWindowsHookEx(WH_KEYBOARD_LL,keyProc,nullptr,0);
    if(mModel | eMouse)
        mouseHook = SetWindowsHookEx(WH_MOUSE_LL,mouseProc,nullptr,0);
}

void MyHook::unInstallHook(Model model)    //删除钩子函数
{
    if(model | eKey)
        UnhookWindowsHookEx(keyHook);
    if(model | eMouse)
        UnhookWindowsHookEx(mouseHook);
    keyHook = nullptr;
    mouseHook = nullptr;
    mModel = eNull;
}

使用

封装为单例模式,使用就相当简单,直接使用instance静态函数,调用installHook函数设置模式即可。如果有自定义信号,可绑定你自己定义的信号槽。
如果只是运行看一下效果,可以直接在main函数或者mainwindow构造函数中加入以下代码:

MyHook::instance()->installHook(MyHook::eMouseKey);
//可使用MyHook::instance()指针,与其他的类绑定信号槽

调试运行输出:
在这里插入图片描述

最后

以上为鼠标、键盘钩子应用,其实在类中还可以添加其他的钩子实现其他目的,windows中钩子的类型有0–14 共15种,具体可可以参考下面文章:
https://blog.csdn.net/mid_Faker/article/details/112569461
代码定义中如下:

#define WH_JOURNALRECORD 0
#define WH_JOURNALPLAYBACK 1
#define WH_KEYBOARD 2
#define WH_GETMESSAGE 3
#define WH_CALLWNDPROC 4
#define WH_CBT 5
#define WH_SYSMSGFILTER 6
#define WH_MOUSE 7
#define WH_HARDWARE 8
#define WH_DEBUG 9
#define WH_SHELL 10
#define WH_FOREGROUNDIDLE 11
#define WH_CALLWNDPROCRET 12

#define WH_KEYBOARD_LL 13
#define WH_MOUSE_LL 14
  • 4
    点赞
  • 11
    收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:技术黑板 设计师:CSDN官方博客 返回首页
评论

打赏作者

码肥人壮

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值