QT隐藏窗口的按键监听

QT隐藏窗口的按键监听,第1张

QT隐藏窗口的按键监听

文章目录
      • 声明
      • 场景
      • 钩子函数实现
        • 分析思路
        • 创建一个传递信号的类
        • 钩子函数
        • 信号连接
      • 参考


声明

本文仅供参考,只是我的个人笔记记录


场景

按下F1按键后隐藏窗口,这个时候失去窗口焦点,再按下按键,应用也是无法捕捉到按键事件的,但是怎么窗口隐藏后怎么打开呢?


钩子函数实现 分析思路

首先是分析需求,需要在窗口隐藏之后监听事件,调用消息钩子函数监听所有的按键事件,然后做一个处理

使用connect函数连接两个两者,一个是新建的类对象,获取到按下的按键值,一个是lambda函数接收按键值参数,然后进行一个处理

那么这个连接可以写在窗体初始化完成时,也可以写在隐藏后就连接,前者是不隐藏窗口也是有效的,后者是只有在窗口隐藏之后才有效

什么时候触发呢?存在按键按下的时候

什么时候有按键按下呢?钩子函数监听到按键事件调用回调函数的时候

那么思路就清晰了


创建一个传递信号的类

新建C++类,生成capturer.h和capturer.cpp

在capturer.h中声明信号函数,信号函数需要有一个按键值参数,方便传递给lambda函数

capturer.h
-----------------------------------------------
#ifndef CAPTURER_H
#define CAPTURER_H

#include 

class capturer : public QObject
{
    Q_OBJECT
public:
    explicit capturer(QObject *parent = nullptr);

    // 这里相当于声明了一个成员变量
    static capturer*& instance()
    {
        static capturer *s = nullptr;
        if (s == nullptr)
            s = new capturer();
        return s;
    }

    void setKeyValue(int key);

signals:
    // 定义一个信号,只需要声明,不需要定义
    void getkey(int key);
};

#endif // CAPTURER_H
-----------------------------------------------
前面static部分相当于直接生成一个capturer对象成员,比如可以像这样无限套娃
capturer::instance()->instance()->instance();

setKeyValue()是触发信号的函数,钩子回调函数是没有办法直接触发信号的
y
声明一个信号函数getkey()
-----------------------------------------------
capturer.cpp
-----------------------------------------------
#include "capturer.h"

capturer::capturer(QObject *parent) : QObject(parent)
{

}

void capturer::setKeyValue(int key)
{
    // 触发信号函数,信号函数携带一个key参数给 槽函数
    emit getkey(key);
}

-----------------------------------------------
setKeyValue()是触发信号的函数,钩子回调函数是没有办法直接触发信号的,触发函数把key给信号函数,信号函数把key传给槽函数
-----------------------------------------------

钩子函数

这一部分写在已经存在的文件中也是没有问题的,但是为了方便处理,建议卸载新建的一个类中

新建C++类,生成keymonitor.h和keymonitor.cpp

keymonitor.h
-----------------------------------------------
#ifndef KEYMONITOR_H
#define KEYMONITOR_H

#include 
#include 

int  startHook();
bool stopHook();

class keymonitor : public QObject
{
    Q_OBJECT
public:
    explicit keymonitor(QObject *parent = nullptr);
signals:

};

#endif // KEYMONITOR_H
-----------------------------------------------
定义了两个全局的函数,注意是全局的,不要放在类里面

一个是启动钩子函数的,钩子函数的使用是需要注册

一个是停止钩子函数的
-----------------------------------------------
keymonitor.cpp
-----------------------------------------------
#include "keymonitor.h"
#include 
#include 

HHOOK hHook = NULL;

keymonitor::keymonitor(QObject *parent) : QObject(parent)
{

}


// 定义钩子函数,返回值是LRESULT
// lParam 和 wParam 是宏定义,一般在消息函数中带这两个类型的参数,通常用来存储窗口消息的参数
LRESULT KeyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    KBDLLHOOKSTRUCT *Key_info = (KBDLLHOOKSTRUCT *)lParam;
    // 判断调用钩子函数的类型
    if (nCode == HC_ACTION)
    {
        // 是按键按下的类型
        if (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN)
        {
            // 判断键值
            if (Key_info->vkCode <= 107 && Key_info->vkCode >= 65)
            {
                if (capturer::instance())
                {
                    // 设置按键值
                    capturer::instance()->setKeyValue(Key_info->vkCode);
                }
            }
        }
    }
    return CallNextHookEx(hHook, nCode, wParam, lParam);
}

HMODULE ModuleFromAddress(PVOID pv)
{
    MEMORY_BASIC_INFORMATION mbi;
    if (VirtualQuery(pv, &mbi, sizeof(mbi)) != 0)
    {
        return (HMODULE)mbi.Allocationbase;
    } else {
        return NULL;
    }
}

int startHook()
{
    // 注册消息钩子函数,返回钩子子程的句柄(对应的还有事件钩子函数)
    // 参数1:钩子的类型,就是它处理的消息类型;
    // 参数2:回调函数;
    // 参数3:应用程序所在DLL实例的句柄。标识包含lpfn所指的子程的DLL的句柄;
    // 参数4:钩子子程序相关的线程的标识符,为0是全局钩子,所有线程
    hHook = SetWindowsHookExW(WH_KEYBOARD_LL, KeyboardHookProc, ModuleFromAddress((PVOID)KeyboardHookProc), 0);
    int error = GetLastError();
    return error;
}

bool stopHook()
{
    return UnhookWindowsHookEx(hHook);
}
-----------------------------------------------

看到startHook()函数,钩子函数的注册,使用之前需要先注册,注册使用函数SetWindowsHookExW()注册钩子函数返回的句柄需要时全局变量,在其他函数需要使用到

参数在上面都有写出来,比如钩子函数监听的消息类型,WH_KEYBOARD_LL是按键消息
写入回调函数,参数3不是很清楚,参数4是可以理解为监听的范围

回调函数KeyboardHookProc()
参数1:nCode表明钩子函数监听的消息和什么相关,参数wParam和lParam包含关于按键消息的信息
参数2和参数3:lParam 和 wParam 是宏定义,一般在消息函数中带这两个类型的参数,通常用来存储窗口消息的参数,前者是键盘消息的类型,WM_KEYDOWN表示键盘消息,包括Ctrl和Shift的组合按键,但是不包括Atl组合按键,WM_SYSKEYDOWN是Atl的组合按键消息

判断是否存在capturer::instance()成员对象,如果存在,调用信号触发函数,把监听到的按键值传递给触发函数

ModuleFromAddress()是关于虚拟内存的,但是具体干嘛的不知道

stopHook()是停止钩子函数
-----------------------------------------------

信号连接

在主窗口中连接信号

widget.cpp
-----------------------------------------------
    connect(capturer::instance(), &capturer::getkey, [=](int key){
        if (key == 101)
            this->show();
    });
    startHook();
-----------------------------------------------
信号的发出者就是可以无限套娃的成员对象,也可以正常的去定义一个全局对象
信号函数时该对象的getkey(),里面有一个参数
接收槽函数时lambda表达式,有一个接收参数int key,接收之后显示窗口,101是数字键盘的5按键
然后就是调用钩子函数的开始注册
-----------------------------------------------

参考

参考1:Qt获取全局键盘消息!检测程序外部和内部按键状态!
参考2:qt之在任意情况获取全局键盘按下事件

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/zaji/5099055.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-11-17
下一篇2022-11-16

发表评论

登录后才能评论

评论列表(0条)

    保存