
- 声明
- 场景
- 钩子函数实现
- 分析思路
- 创建一个传递信号的类
- 钩子函数
- 信号连接
- 参考
声明
本文仅供参考,只是我的个人笔记记录
场景
按下F1按键后隐藏窗口,这个时候失去窗口焦点,再按下按键,应用也是无法捕捉到按键事件的,但是怎么窗口隐藏后怎么打开呢?
钩子函数实现 分析思路
首先是分析需求,需要在窗口隐藏之后监听事件,调用消息钩子函数监听所有的按键事件,然后做一个处理
使用connect函数连接两个两者,一个是新建的类对象,获取到按下的按键值,一个是lambda函数接收按键值参数,然后进行一个处理
那么这个连接可以写在窗体初始化完成时,也可以写在隐藏后就连接,前者是不隐藏窗口也是有效的,后者是只有在窗口隐藏之后才有效
什么时候触发呢?存在按键按下的时候
什么时候有按键按下呢?钩子函数监听到按键事件调用回调函数的时候
那么思路就清晰了
创建一个传递信号的类
新建C++类,生成capturer.h和capturer.cpp
在capturer.h中声明信号函数,信号函数需要有一个按键值参数,方便传递给lambda函数
capturer.h ----------------------------------------------- #ifndef CAPTURER_H #define CAPTURER_H #includeclass 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之在任意情况获取全局键盘按下事件
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)