QT 使用全局钩子监听鼠标事件和键盘事件
简介
- include <windows.h>
- 本版本使用的LL版,提供了获取虚拟键码,鼠标坐标等方法,比较全面。
- 采用了观察者模式,将所有注册进来的函数放置到list里面,当事件触发的时候遍历和调用。
- hookhelper.h 和 hookhelper.cpp 考虑了移植性和多线程,所以写的复杂了许多,但是功能是全面的,且做成DLL也是比较方便的。
- 重要文档
- SetWindowsHookExA
- 键盘监听: WH_KEYBOARD_LL
- LPARAM: PKBDLLHOOKSTRUCT
- 鼠标监听: WH_MOUSE_LL
- LPARAM: PMSLLHOOKSTRUCT
- 键盘监听: WH_KEYBOARD_LL
- SetWindowsHookExA
源代码
hookhelper.h
#ifndef HOOKHELPER_H
#define HOOKHELPER_H#include <iostream>
#include <Windows.h>
#include <string>
#include <list>
#include <mutex>
using namespace std;enum class e_WParam;
typedef void (*f_keyEvent )(e_WParam wParam, LPARAM lParam);
typedef void (*f_print)(string str);
// 键盘消息
struct s_KeyCallMsg{string name;WPARAM listenWParam;f_keyEvent callback;
};
// 鼠标消息
struct s_MouseCallMsg{string name;WPARAM listenWParam;f_keyEvent callback;
};
// HOOK的种类
enum class e_HookType{MOUSE,KEY_BOARD
};
// 触发的事件
enum class e_WParam{// 按键KEYDOWN=0x0100,KEYUP=0x0101,SYSKEYDOWN=0x0104,SYSKEYUP=0x0105,// 鼠标LBUTTONUP=0x0202,LBUTTONDOWN=0x0201,RBUTTONDOWN=0x0204,RBUTTONUP=0x0205,// 滚动事件MOUSEHWHEEL=0x020E,MOUSEWHEEL=0x020A,// 鼠标移动MOUSEMOVE=0x0200
};
/*** @brief 获取虚拟键码* @param lParam* @return*/
DWORD GetKeyCode(LPARAM lParam);
/*** @brief 获取鼠标位置* @param lParam* @return*/
POINT GetMousePoint(LPARAM lParam);
/*** @brief 设置打印函数* @param f*/
void SetPrintFunction(f_print f);
/*** @brief 添加新的键盘回调函数* @param callMsg* @return*/
BOOL AddKeyBoardHookCallBack(s_KeyCallMsg callMsg);
/*** @brief 添加新的鼠标回调函数* @param callMsg* @return*/
BOOL AddMouseHookCallBack(s_MouseCallMsg callMsg);
/*** @brief 卸载钩子* @param name 钩子名称* @param type 钩子类型* @return*/
BOOL UninstallHook(string name,e_HookType type);
/*** @brief 卸载所有的钩子* @param type 钩子类型* @return*/
BOOL UninstallAllHook(e_HookType type) ;#endif // HOOKHELPER_H
hookhelper.cpp
#include "hookhelper.h"
// 必要的静态链接库
//#pragma comment (lib,"User32.lib")
//#pragma comment (lib,"Gdi32.lib")// 配置打印方式
HHOOK g_keyHHook;
HHOOK g_mouseHHook;
list<s_KeyCallMsg> g_keyCallMsgList;
list<s_MouseCallMsg> g_mouseCallMsgList;
BOOL g_keyIsInstall=FALSE;
BOOL g_mouseIsInstall=FALSE;
f_print g_print = NULL;
static mutex g_keyMu; // 线程锁
static mutex g_mouseMu; // 线程锁void _PrivateLog(string str){if(g_print==NULL){return;}(*g_print)(str);
}/*** @brief 获取虚拟键码* @param lParam* @return*/
DWORD GetKeyCode(LPARAM lParam){PKBDLLHOOKSTRUCT st = (PKBDLLHOOKSTRUCT)lParam;return st->vkCode;
}
/*** @brief 获取鼠标位置* @param lParam* @return*/
POINT GetMousePoint(LPARAM lParam){PMSLLHOOKSTRUCT st =(PMSLLHOOKSTRUCT)lParam;return st->pt;
}/// <summary>
/// 键盘回调
/// </summary>
/// <param name="code"></param>
/// <param name="wParam"></param>
/// <param name="lParam"></param>
/// <returns></returns>
LRESULT CALLBACK ProcForKeyBoard(int code, WPARAM wParam, LPARAM lParam) {if (code < 0 || code == HC_NOREMOVE) {// 如果代码小于零,则挂钩过程必须将消息传递给CallNextHookEx函数,而无需进一步处理,并且应返回CallNextHookEx返回的值。此参数可以是下列值之一。(来自官网手册)return CallNextHookEx(g_keyHHook, code, wParam, lParam);}DWORD vkCode = GetKeyCode(lParam);for(auto it=g_keyCallMsgList.begin();it!=g_keyCallMsgList.end();it++){s_KeyCallMsg callMsg = *it;if(callMsg.listenWParam == 0){// 监听任何一个回调callMsg.callback(e_WParam(wParam),lParam);}else if(callMsg.listenWParam == vkCode){// 监听指定的回调callMsg.callback(e_WParam(wParam),lParam);}}// 0x0200// 将钩子往下传return CallNextHookEx( g_keyHHook, code, wParam, lParam);
}/// <summary>
/// 键盘回调
/// </summary>
/// <param name="code"></param>
/// <param name="wParam"></param>
/// <param name="lParam"></param>
/// <returns></returns>
LRESULT CALLBACK ProcForMouseBoard(int code, WPARAM wParam, LPARAM lParam) {if (code < 0 || code == HC_NOREMOVE) {// 如果代码小于零,则挂钩过程必须将消息传递给CallNextHookEx函数,而无需进一步处理,并且应返回CallNextHookEx返回的值。此参数可以是下列值之一。(来自官网手册)return CallNextHookEx(g_mouseHHook, code, wParam, lParam);}for(auto it=g_mouseCallMsgList.begin();it!=g_mouseCallMsgList.end();it++){s_MouseCallMsg callMsg = *it;if(callMsg.listenWParam == 0){// 监听任何一个回调callMsg.callback(e_WParam(wParam),lParam);}else if(callMsg.listenWParam == wParam){// 监听指定的回调callMsg.callback(e_WParam(wParam),lParam);}}// 0x0200// 将钩子往下传return CallNextHookEx( g_mouseHHook, code, wParam, lParam);
}/*** @brief 设置打印函数* @param f*/
void SetPrintFunction(f_print f){g_print = f;
}/*** @brief 安装钩子* @return*/
BOOL InstallHook(e_HookType type) {BOOL flag = FALSE;if(type==e_HookType::KEY_BOARD){// 键盘回调事件if(!g_keyIsInstall){g_keyMu.lock();// 两次判断防止出现线程安全问题if(!g_keyIsInstall){// 【参数1】钩子的类型,这里代表键盘钩子// 【参数2】钩子处理的函数// 【参数3】如果是DLL项目得写 GetModuleHandle("dll文件名"),如果应用程序,直接写 GetModuleHandle(NULL)或者nullptr都行// 【参数4】线程的ID,如果是全局钩子的话,这里要填0,如果是某个线程的钩子,那就需要写线程的IDg_keyHHook = SetWindowsHookEx(WH_KEYBOARD_LL, ProcForKeyBoard, nullptr, 0);if ( g_keyHHook == NULL) {// 钩子安装失败_PrivateLog("全局钩子注册失败");flag = FALSE;g_keyIsInstall = FALSE;}else{flag = TRUE;g_keyIsInstall = TRUE;}}g_keyMu.unlock();}}else if(type == e_HookType::MOUSE){// 鼠标回调事件if(!g_mouseIsInstall){g_mouseMu.lock();if(!g_mouseIsInstall){// 【参数1】钩子的类型,这里代表键盘钩子// 【参数2】钩子处理的函数// 【参数3】如果是DLL项目得写 GetModuleHandle("dll文件名"),如果应用程序,直接写 GetModuleHandle(NULL)或者nullptr都行// 【参数4】线程的ID,如果是全局钩子的话,这里要填0,如果是某个线程的钩子,那就需要写线程的IDg_mouseHHook = SetWindowsHookEx(WH_MOUSE_LL, ProcForMouseBoard, GetModuleHandle(NULL), 0);if ( g_mouseHHook == NULL) {// 钩子安装失败_PrivateLog("全局钩子注册失败");flag = FALSE;g_mouseIsInstall = FALSE;}else{flag = TRUE;g_mouseIsInstall = TRUE;}}g_mouseMu.unlock();}}return flag;
}
/*** @brief 添加新的回调函数* @param callMsg* @return*/
BOOL AddKeyBoardHookCallBack(s_KeyCallMsg callMsg){BOOL flag = InstallHook(e_HookType::KEY_BOARD);if(!flag){// 安装钩子失败return FALSE;}// 安装成功g_keyCallMsgList.push_back(callMsg);return TRUE;
}
/*** @brief 添加新的回调函数* @param callMsg* @return*/
BOOL AddMouseHookCallBack(s_MouseCallMsg callMsg){BOOL flag = InstallHook(e_HookType::MOUSE);if(!flag){// 安装钩子失败return FALSE;}// 安装成功g_mouseCallMsgList.push_back(callMsg);return TRUE;
}/*** @brief 卸载钩子* @param name 钩子名称* @param type 钩子类型* @return*/
BOOL UninstallHook(string name,e_HookType type) {BOOL unFlag = FALSE;switch(type){case e_HookType::KEY_BOARD:// 卸载键盘钩子g_keyMu.lock();if(g_keyIsInstall){size_t size = g_keyCallMsgList.size();if(size==1){unFlag = UnhookWindowsHookEx( g_keyHHook);if(unFlag){g_keyCallMsgList.clear();}g_keyIsInstall = unFlag ? FALSE : TRUE;}else if(size>1){// 找到要卸载的钩子的名称for(auto it=g_keyCallMsgList.begin();it!=g_keyCallMsgList.end();it++){s_KeyCallMsg callMsg = *it;if(callMsg.name == name){g_keyCallMsgList.erase(it);break;}}unFlag=TRUE;}}else{// 钩子已经被卸载unFlag = TRUE;}g_keyMu.unlock();return unFlag;case e_HookType::MOUSE:// 卸载鼠标钩子g_mouseMu.lock();if(g_mouseIsInstall){size_t size = g_mouseCallMsgList.size();if(size==1){unFlag = UnhookWindowsHookEx(g_mouseHHook);if(unFlag){g_mouseCallMsgList.clear();}g_mouseIsInstall = unFlag ? FALSE : TRUE;}else if(size>1){for(auto it=g_mouseCallMsgList.begin();it!=g_mouseCallMsgList.end();it++){s_MouseCallMsg callMsg = *it;if(callMsg.name == name){g_mouseCallMsgList.erase(it);break;}}unFlag=TRUE;}}else{// 钩子已经被卸载unFlag = TRUE;}g_mouseMu.unlock();return unFlag;default:return TRUE;}
}/*** @brief 卸载所有的钩子* @param name 钩子名称* @param type 钩子类型* @return*/
BOOL UninstallAllHook(e_HookType type) {BOOL unFlag = FALSE;switch(type){case e_HookType::KEY_BOARD:// 卸载键盘钩子g_keyMu.lock();if(g_keyIsInstall){unFlag = UnhookWindowsHookEx( g_keyHHook);if(unFlag){g_keyCallMsgList.clear();}g_keyIsInstall = unFlag ? FALSE : TRUE;}else{// 钩子已经被卸载unFlag = TRUE;}g_keyMu.unlock();return unFlag;case e_HookType::MOUSE:// 卸载鼠标钩子g_mouseMu.lock();if(g_mouseIsInstall){unFlag = UnhookWindowsHookEx(g_mouseHHook);if(unFlag){g_mouseCallMsgList.clear();}g_mouseIsInstall = unFlag ? FALSE : TRUE;}else{// 钩子已经被卸载unFlag = TRUE;}g_mouseMu.unlock();return unFlag;default:return TRUE;}
}
QT 中使用示例
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "hookhelper.h"void keyEvent(e_WParam wParam, LPARAM lParam){DWORD keyCode;switch(wParam){case e_WParam::KEYUP:keyCode = GetKeyCode(lParam);qDebug()<< keyCode <<",按键松开了";break;case e_WParam::KEYDOWN:qDebug()<<"按键按下了";break;case e_WParam::SYSKEYDOWN:qDebug()<<"系统按键按下了";break;case e_WParam::SYSKEYUP:qDebug()<<"按键松开了";break;}
}
void mouseEvent(e_WParam wParam, LPARAM lParam){POINT p;switch(wParam){case e_WParam::LBUTTONUP:p = GetMousePoint(lParam);qDebug()<< p.x << "," << p.y<<",鼠标左键松开了";break;case e_WParam::LBUTTONDOWN:qDebug()<<"鼠标左键按下了";break;case e_WParam::RBUTTONDOWN:qDebug()<<"鼠标右键按下了";break;case e_WParam::RBUTTONUP:qDebug()<<"鼠标右键松开了";break;}
}void _print(std::string str){qDebug() << "输出:"<< QString::fromLocal8Bit(str.data());
}MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);SetPrintFunction(_print);
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::on_pushButton_clicked()
{// 设置传送信息s_KeyCallMsg callMsg;callMsg.name = "keyCall";callMsg.callback = keyEvent;callMsg.listenWParam = 0;BOOL flag = AddKeyBoardHookCallBack(callMsg);qDebug() << "Hook结果" << flag;
}void MainWindow::on_pushButton_3_clicked()
{// 设置传送信息s_MouseCallMsg callMsg;callMsg.name = "mouseCall";callMsg.callback = mouseEvent;callMsg.listenWParam = 0;BOOL flag = AddMouseHookCallBack(callMsg);qDebug() << "Hook结果" << flag;
}void MainWindow::on_pushButton_2_clicked()
{UninstallAllHook(e_HookType::KEY_BOARD);UninstallAllHook(e_HookType::MOUSE);
}
QT 使用全局钩子监听鼠标事件和键盘事件相关推荐
- 综合设计一个OPPE主页--页面的插件引用(animate.css)--d动画的使用--滚轮或鼠标到该位置时,才有动画的切换---所以我们需要用jquery监听鼠标滚轮的滚动事件
Animate.css | A cross-browser library of CSS animations. 里面有许多css的效果 首先使用 animate.css文件 link rel=&qu ...
- java 监听鼠标点击_java 事件监听 - 鼠标
java 事件监听 - 鼠标 //事件监听 //鼠标事件监听 //鼠标事件监听有两个实现接口 //1.MouseListener 普通的鼠标操作 //2.MouseMotionListener 鼠标的 ...
- css 鼠标滚动事件,js监听鼠标的滚轮滚动事件教程
不同的有不同的滚轮事件.主要是有两种,onmousewheel(firefox不支持)和dommousescroll(只有firefox支持),关于这两个事件这里不做详述,想要了解的朋友请移步:鼠标滚 ...
- esc键退出全屏 vue_解决了VUE在浏览器全屏下监听不到Esc键盘事件
说明: 实测可以在谷歌.火狐.360 浏览器使用 解决了在浏览器全屏下监听不到键盘Esc事件 解决了取消全屏和全屏的同步问题,ESC按键下可以同步 以下是完整的代码, // data() { retu ...
- 解决VUE在浏览器全屏下监听不到Esc键盘事件
实测可以在谷歌.火狐.360 浏览器使用 解决了在浏览器全屏下监听不到键盘Esc事件 解决了取消全屏和全屏的同步问题,ESC按键下可以同步 以下是完整的代码, // data() {return {i ...
- vuejs监听苹果iphone手机键盘事件
在iphone手机中,vue提供的keyup事件是不能监听iphone键盘的,但是h5提供的input事件可以做到. 只需要向下面这样处理,就可以解决iphone不响应键盘事件的bug <tem ...
- 鼠标移入事件_NSTrackingArea 监听鼠标移入与移出事件
在NSView中: var area:NSTrackingArea! override func updateTrackingAreas() {if area != nil {self.removeT ...
- python pyhook监听扫码_Python——pyHook监听鼠标键盘事件
pyHook包为Windows中的全局鼠标和键盘事件提供回调. 底层C库报告的信息包括事件的时间,事件发生的窗口名称,事件的值,任何键盘修饰符等. 而正常工作需要pythoncom等操作系统的API的 ...
- python监听鼠标事件_Python中使用PyHook监听鼠标和键盘事件实例
Python 中使用 PyHook 监听鼠标和键盘事件实例 PyHook 是一个基于 Python 的"钩子"库,主要用于监 听当前电脑上鼠标和键盘的事件.这个库依赖于另一个 Py ...
- python获取键盘事件_50-用Python监听鼠标和键盘事件
PyHook是一个基于Python的"钩子"库,主要用于监听当前电脑上鼠标和键盘的事件.这个库依赖于另一个Python库PyWin32,如同名字所显示的,PyWin32只能运行在W ...
最新文章
- 查看当前正在运行的python进程
- 如何优化linux服务器,手把手教你如何优化linux服务器
- php神盾 var 1.54,PHP变量可用字符 - 神盾加密解密教程(一)
- MVC4做网站后台:栏目管理1、添加栏目
- ubuntu安装node.js
- Apollo之灰度发布
- 设计模式:依赖倒置原则
- bzoj2242 [SDOI2011]计算器 exgcd+ksm+bsgs
- php5d,php挖洞基础知识篇以及防范方法
- Python:hashlib加密,flask模块写接口
- 位置度标注方法图解_追踪主力-散户操盘实战图解:操盘手法分析
- 浩哥带你学习JDK1.1源码——第1天
- 认识网络通信中的 ACK、NACK 和 REX
- 2007年五一通过了驾驶证考试(5/7,5/8)
- 仓库实现降本增效的秘密法宝,WMS智能仓储系统
- c++虚函数实现原理
- Java中合理使用局部变量替代成员变量、静态变量
- 2022年各大高校最新博士薪资汇总~
- 滤波电容为什么要靠近放置,储能电容为什么均匀放置?去耦半径是什么?滤波电容如何打孔?
- Azure Kinect DK 基本开发流程