钩子(Hook)实际上是一个处理消息的程序段,通过系统调用,把它挂入系统。每当特定的消息发出,在没有到达目的窗口前,钩子程序就先捕获该消息,亦即钩子函数先得到控制权。这时钩子函数即可以加工处理(改变)该消息,也可以不作处理而继续传递该消息,还可以强制结束消息的传递。
钩子技术分为系统钩子技术和线程钩子技术
HHOOK SetWindowsHookExA(
[in] int idHook,
[in] HOOKPROC lpfn,
[in] HINSTANCE hmod,
[in] DWORD dwThreadId
);
idHook:钩子类型(-1~14)
lpfn:指向钩子进程的指针。如果dwThreadId参数为零或指定了由其他进程创建的线程的标识符,那么lpfn参数必须指向DLL中的钩子进程。否则,lpfn可以指向与当前进程关联的代码中的钩子进程。
hmod:lpfn形参指向的钩子子程所在DLL的句柄。 如果dwThreadId参数指定了一个由当前进程创建的线程,并且钩子子程在与当前进程相关的代码中,那么hMod参数必须设置为NULL。
dwThreadId:钩子子程要关联的线程的标识符。对于桌面应用程序,如果该参数为0,则钩子子程与调用线程在同一桌面中运行的所有现有线程相关联。对于Windows Store应用程序,请参见备注部分。
HHOOK:如果函数执行成功,返回值是钩子子程的句柄。
如果函数失败,返回值是NULL。
在winuser.h中定义了
#define WH_MSGFILTER (-1)
#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 // 监视各种Shell事件消息。比如启动和关闭应用程序
#define WH_FOREGROUNDIDLE 11
#define WH_CALLWNDPROCRET 12 // 监视所有从系统消息队列发往目标窗口的消息
#define WH_KEYBOARD_LL 13 // 底层键盘输入(不依赖本程序)
#define WH_MOUSE_LL 14 // 底层鼠标输入(不依赖本程序)
LRESULT CallNextHookEx(
[in, optional] HHOOK hhk,
[in] int nCode,
[in] WPARAM wParam,
[in] LPARAM lParam
);
hhk:忽略
nCode:传递给当前钩子进程的钩子代码。下一个钩子进程使用此代码来确定如何处理该钩子信息。
wParam:传递给当前钩子进程的wParam值。该参数的含义取决于与当前钩子链关联的钩子类型。
lParam:传递给当前钩子进程的lParam值。该参数的含义取决于与当前钩子链关联的钩子类型。
LRESULT:返回值,值由链表中的下一个钩子进程返回。当前钩子进程也必须返回该值。返回值的含义取决于钩子的类型。
BOOL UnhookWindowsHookEx(
[in] HHOOK hhk
);
hhk:前一个调用SetWindowsHookEx获得的钩子句柄。
BOOL:删除成功与否
注:为了程序简洁,只监控键盘输入0和1。
完整程序参考:
https://blog.csdn.net/Simon798/article/details/98848050
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include <iostream>
#include <stdio.h>
#include <conio.h>
using namespace std;
HHOOK keyboardHook = 0; // 钩子句柄、
LRESULT CALLBACK LowLevelKeyboardProc(
_In_ int nCode, // 规定钩子如何处理消息,小于 0 则直接 CallNextHookEx
_In_ WPARAM wParam, // 消息类型
_In_ LPARAM lParam // 指向某个结构体的指针,这里是 KBDLLHOOKSTRUCT(低级键盘输入事件)
){
KBDLLHOOKSTRUCT *ks = (KBDLLHOOKSTRUCT*)lParam; // 包含低级键盘输入事件信息
/*
typedef struct tagKBDLLHOOKSTRUCT {
DWORD vkCode; // 按键代号
DWORD scanCode; // 硬件扫描代号,同 vkCode 也可以作为按键的代号。
DWORD flags; // 事件类型,一般按键按下为 0 抬起为 128。
DWORD time; // 消息时间戳
ULONG_PTR dwExtraInfo; // 消息附加信息,一般为 0。
}KBDLLHOOKSTRUCT,*LPKBDLLHOOKSTRUCT,*PKBDLLHOOKSTRUCT;
*/
if(ks->flags == 128 || ks->flags == 129)
{
// 监控键盘
switch(ks->vkCode){
case 0x30: case 0x60:
cout << "检测到按键:" << "0" << endl;
break;
case 0x31: case 0x61:
cout << "检测到按键:" << "1" << endl;
break;
}
//return 1; // 使按键失效
}
// 将消息传递给钩子链中的下一个钩子
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
int _tmain(int argc, _TCHAR* argv[])
{
SetConsoleOutputCP(65001);// 更改cmd编码为utf8
// 安装钩子
keyboardHook = SetWindowsHookEx(
WH_KEYBOARD_LL, // 钩子类型,WH_KEYBOARD_LL 为键盘钩子
LowLevelKeyboardProc, // 指向钩子函数的指针
GetModuleHandleA(NULL), // Dll 句柄
NULL
);
if (keyboardHook == 0){cout << "挂钩键盘失败" << endl; return -1;}
//不可漏掉消息处理,不然程序会卡死
MSG msg;
while(1)
{
// 如果消息队列中有消息
if (PeekMessageA(
&msg, // MSG 接收这个消息
NULL, // 检测消息的窗口句柄,NULL:检索当前线程所有窗口消息
NULL, // 检查消息范围中第一个消息的值,NULL:检查所有消息(必须和下面的同时为NULL)
NULL, // 检查消息范围中最后一个消息的值,NULL:检查所有消息(必须和上面的同时为NULL)
PM_REMOVE // 处理消息的方式,PM_REMOVE:处理后将消息从队列中删除
)){
// 把按键消息传递给字符消息
TranslateMessage(&msg);
// 将消息分派给窗口程序
DispatchMessageW(&msg);
}
else
Sleep(0); //避免CPU全负载运行
}
// 删除钩子
UnhookWindowsHookEx(keyboardHook);
return 0;
}
程序运行情况参考:(支持后台监控)