演示平台:windows。
编译环境:Qt5.12.2 MinGW 64-bit
Windows API:
///加载钩子
/**
* SetWindowsHookEx 函数解释
* int idHook 所监控的挂钩类型
* HOOKPROC lpfn 监控信息的处理函数
* HINSTANCEhMod 监控信息的动态链接位置 nullptr则与本线程相关
* DWORD dwThreadId 挂钩线程id 0则代表当前 决定了此钩子是系统钩子还是线程钩子
* 返回值 函数执行成功,则返回值就是该挂钩处理过程的句柄;若此函数执行失败,则返回值为NULL(0)
*/
WINUSERAPI HHOOK WINAPI SetWindowsHookExW (int idHook, HOOKPROC lpfn, HINSTANCE hmod, DWORD dwThreadId);
///SetWindowsHookExW的回调函数
/**
* @brief Hookproc
* @param code 这是一个整数值,用于指定发送到钩子过程的消息类型。它可以是几个值中的一个,例如HC_ACTION、HC_NOREMOVE或HC_CREATE
* @param wParam 一个指向宽字符值的指针,其中包含有关发送到钩子过程的消息的附加信息,其值取决于code的值
* @param lParam 一个指向长字符值的指针,其中包含有关发送到钩子过程的消息的附加信息,其值取决于code的值
* @return 如果返回0,则消息未被处理,将继续由其他钩子或目标窗口处理。如果返回非零值,则消息已被钩子过程处理,不会被其他钩子或目标窗口处理
*/
LRESULT Hookproc2(int code, WPARAM wParam, LPARAM lParam)
// 卸载钩子
WINUSERAPI WINBOOL WINAPI UnhookWindowsHookEx (HHOOK hhk);
WH_KEYBOARD_LL:键盘监测
WH_MOUSE_LL:鼠标检测
示例:
代码:
chook.h
#ifndef CHOOK_H
#define CHOOK_H
class Widget;
class CHook
{
public:
CHook(Widget* w);
~CHook();
};
#endif // CHOOK_H
chook.cpp
#include "chook.h"
#include <windows.h>
#include "widget.h"
#include <QDebug>
Widget* g_w;
HHOOK g_hook1; // 钩子对象
HHOOK g_hook2; // 钩子对象
/**
* @brief Hookproc
* @param code 这是一个整数值,用于指定发送到钩子过程的消息类型。它可以是几个值中的一个,例如HC_ACTION、HC_NOREMOVE或HC_CREATE
* @param wParam 一个指向宽字符值的指针,其中包含有关发送到钩子过程的消息的附加信息,其值取决于code的值
* @param lParam 一个指向长字符值的指针,其中包含有关发送到钩子过程的消息的附加信息,其值取决于code的值
* @return 如果返回0,则消息未被处理,将继续由其他钩子或目标窗口处理。如果返回非零值,则消息已被钩子过程处理,不会被其他钩子或目标窗口处理
*/
LRESULT Hookproc1(int code, WPARAM wParam, LPARAM lParam)
{
// lParam强转为键盘数据结构体
KBDLLHOOKSTRUCT *data = (KBDLLHOOKSTRUCT *)lParam;
//! GetAsyncKeyState
//! 获取指定按钮状态:非0则为按下状态,为0则为未按下状态
// 当Ctrl、Alt、X都按下时进入
if(GetAsyncKeyState(VK_LCONTROL) && GetAsyncKeyState(VK_LMENU) && 0x58 == data->vkCode)
{
g_w->setText("组合按钮按下:Ctrl+Alt+x");
}
else if(GetAsyncKeyState(VK_LCONTROL) && 0x5a == data->vkCode)
{
g_w->setText("组合按钮按下:Ctrl+z");
}
else{
QString txt;
if (data->flags == 128 || data->flags == 129)
{
// 监控按键状态
if (code >= 0)
{
switch (wParam)
{
case WM_KEYDOWN:
txt = "普通按键抬起" + QString::number(data->vkCode);
break;
case WM_KEYUP:
txt = "普通按鍵按下" + QString::number(data->vkCode);
break;
case WM_SYSKEYDOWN:
txt = "系统按键抬起" + QString::number(data->vkCode);
break;
case WM_SYSKEYUP:
txt = "系统按键按下" + QString::number(data->vkCode);
break;
}
}
// 监控键盘,并判断键
switch (data->vkCode)
{
case VK_F1:
txt = "检测到按键:F1";
break;
case VK_LCONTROL:
txt = "检测到按键:Ctrl";
break;
case VK_LMENU:
txt = "检测到按键:Alt";
break;
case VK_RETURN:
txt = "检测到按键:Enter";
break;
case VK_RSHIFT:
case VK_LSHIFT:
txt = "检测到按键:Shift";
break;
case VK_BACK:
txt = "检测到按键:Backspace";
break;
case VK_SPACE:
txt = "检测到按键:Space";
break;
}
g_w->setText(txt);
}
}
return CallNextHookEx(g_hook1, code, wParam, lParam);
}
LRESULT Hookproc2(int code, WPARAM wParam, LPARAM lParam)
{
MSLLHOOKSTRUCT* data = (MSLLHOOKSTRUCT* )lParam;
POINT pt = data->pt;
DWORD mouseData = data->time;
const char* info = NULL;
char text[60], pData[50], mData[50];
if (code >= 0)
{
if (wParam == WM_MOUSEMOVE)
{
info = "鼠标 [移动]";
}
else if (wParam == WM_LBUTTONDOWN)
{
info = "鼠标 [左键] 按下";
}
else if (wParam == WM_LBUTTONUP)
{
info = "鼠标 [左键] 抬起";
}
else if (wParam == WM_LBUTTONDBLCLK)
{
info = "鼠标 [左键] 双击";
}
else if (wParam == WM_RBUTTONDOWN)
{
info = "鼠标 [右键] 按下";
}
else if (wParam == WM_RBUTTONUP)
{
info = "鼠标 [右键] 抬起";
}
else if (wParam == WM_RBUTTONDBLCLK)
{
info = "鼠标 [右键] 双击";
}
else if (wParam == WM_MBUTTONDOWN)
{
info = "鼠标 [滚轮] 按下";
}
else if (wParam == WM_MBUTTONUP)
{
info = "鼠标 [滚轮] 抬起";
}
else if (wParam == WM_MBUTTONDBLCLK)
{
info = "鼠标 [滚轮] 双击";
}
else if (wParam == WM_MOUSEWHEEL)
{
info = "鼠标 [滚轮] 滚动";
}
ZeroMemory(text, sizeof(text));
ZeroMemory(pData, sizeof(pData));
ZeroMemory(mData, sizeof(mData));
QString txt = QString("鼠标状态:%1,X:%2,Y:%3,附加数据:%4").arg(info).arg(pt.x).arg(pt.y).arg(mouseData);
g_w->setText(txt);
}
return CallNextHookEx(g_hook2, code, wParam, lParam);
}
CHook::CHook(Widget *w)
{
g_w = w;
//! SetWindowsHookEx 函数解释
//! int idHook 所监控的挂钩类型
//! HOOKPROC lpfn 监控信息的处理函数
//! HINSTANCEhMod 监控信息的动态链接位置 nullptr则与本线程相关
//! DWORD dwThreadId 挂钩线程id 0则代表当前 决定了此钩子是系统钩子还是线程钩子
//! 返回值 函数执行成功,则返回值就是该挂钩处理过程的句柄;若此函数执行失败,则返回值为NULL(0)
g_hook1 = SetWindowsHookEx(WH_KEYBOARD_LL, Hookproc1, nullptr, 0);
g_hook2 = SetWindowsHookEx(WH_MOUSE_LL, Hookproc2, nullptr, 0);
}
CHook::~CHook()
{
// 卸载钩子
UnhookWindowsHookEx(g_hook1);
UnhookWindowsHookEx(g_hook2);
}
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include "chook.h"
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = nullptr);
~Widget();
void setText(QString txt);
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QScrollBar>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
ui->textEdit->setReadOnly(true);
}
Widget::~Widget()
{
delete ui;
}
void Widget::setText(QString txt)
{
ui->textEdit->append(txt);
ui->textEdit->verticalScrollBar()->setValue(ui->textEdit->verticalScrollBar()->maximum());
}
main.cpp
#include "widget.h"
#include <QApplication>
#include "chook.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
CHook hook(&w);
w.show();
return a.exec();
}