文章目录
- 1、简介
- 2、相关函数
- 2.1 SetWindowsHookEx
- 2.2 UnhookWindowsHookEx
- 2.3 CallNextHookEx
- 3、相关结构体
- 3.1 KBDLLHOOKSTRUCT
- 3.2 MSLLHOOKSTRUCT
- 4、挂钩过程
- 5、代码测试
- 5.1 代码1
- 结语
1、简介
https://learn.microsoft.com/zh-cn/windows/win32/winmsg/about-hooks
挂钩是应用程序截获消息、鼠标操作和击键等事件的机制。 截获特定类型的事件的函数称为 挂钩过程。 挂钩过程可以对其接收的每个事件执行操作,然后修改或放弃该事件。
挂钩是系统消息处理机制中的一个点,其中应用程序可以安装子例程来监视系统中的消息流量,并在某些类型的消息到达目标窗口过程之前对其进行处理。
以下一些示例使用 挂钩:
- 监视用于调试的消息
- 支持录制和播放宏
- 为帮助密钥 (F1) 提供支持
- 模拟鼠标和键盘输入
- 实现基于计算机的训练 (CBT) 应用程序
2、相关函数
2.1 SetWindowsHookEx
将应用程序定义的挂钩过程安装到挂钩链中。 你将安装挂钩过程来监视系统的某些类型的事件。 这些事件与特定线程或与调用线程位于同一桌面中的所有线程相关联。
HHOOK SetWindowsHookExA(
[in] int idHook,
[in] HOOKPROC lpfn,
[in] HINSTANCE hmod,
[in] DWORD dwThreadId
);
HHOOK SetWindowsHookExW(
[in] int idHook,
[in] HOOKPROC lpfn,
[in] HINSTANCE hmod,
[in] DWORD dwThreadId
);
- WH_KEYBOARD
安装用于监视击键消息的挂钩过程。 有关详细信息,请参阅 KeyboardProc 挂钩过程。
LRESULT CALLBACK KeyboardProc(
_In_ int code,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
- WH_KEYBOARD_LL
安装用于监视低级别键盘输入事件的挂钩过程。 有关详细信息,请参阅 [LowLevelKeyboardProc] (/windows/win32/winmsg/lowlevelkeyboardproc) 挂钩过程。
LRESULT CALLBACK LowLevelKeyboardProc(
_In_ int nCode,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
// 其中lParam指向 KBDLLHOOKSTRUCT 结构的指针。
- WH_MOUSE
安装监视鼠标消息的挂钩过程。 有关详细信息,请参阅 MouseProc 挂钩过程。
LRESULT CALLBACK MouseProc(
_In_ int nCode,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
- WH_MOUSE_LL
安装用于监视低级别鼠标输入事件的挂钩过程。 有关详细信息,请参阅 LowLevelMouseProc 挂钩过程。
LRESULT CALLBACK LowLevelMouseProc(
_In_ int nCode,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
// 其中lParam指向 MSLLHOOKSTRUCT 结构的指针。
2.2 UnhookWindowsHookEx
删除 SetWindowsHookEx 函数安装在挂钩链中的挂钩过程。
BOOL UnhookWindowsHookEx(
[in] HHOOK hhk
);
2.3 CallNextHookEx
将挂钩信息传递给当前挂钩链中的下一个挂钩过程。 挂钩过程可以在处理挂钩信息之前或之后调用此函数。
LRESULT CallNextHookEx(
[in, optional] HHOOK hhk,
[in] int nCode,
[in] WPARAM wParam,
[in] LPARAM lParam
);
3、相关结构体
3.1 KBDLLHOOKSTRUCT
包含有关低级别键盘输入事件的信息。
typedef struct tagKBDLLHOOKSTRUCT {
DWORD vkCode;
DWORD scanCode;
DWORD flags;
DWORD time;
ULONG_PTR dwExtraInfo;
} KBDLLHOOKSTRUCT, *LPKBDLLHOOKSTRUCT, *PKBDLLHOOKSTRUCT;
3.2 MSLLHOOKSTRUCT
包含有关低级别鼠标输入事件的信息。
typedef struct tagMSLLHOOKSTRUCT {
POINT pt;
DWORD mouseData;
DWORD flags;
DWORD time;
ULONG_PTR dwExtraInfo;
} MSLLHOOKSTRUCT, *LPMSLLHOOKSTRUCT, *PMSLLHOOKSTRUCT;
4、挂钩过程
- 定义挂钩过程
为了利用特定类型的挂钩,开发人员提供了一个挂钩过程,并使用 SetWindowsHookEx 函数将其安装到与挂钩关联的链中。 挂钩过程必须具有以下语法:
LRESULT CALLBACK HookProc(
int nCode,
WPARAM wParam,
LPARAM lParam
)
{
// process event
...
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
SetWindowsHookEx 函数始终在挂钩链的开头安装挂钩过程。 当发生由特定类型的挂钩监视的事件时,系统会在与挂钩关联的挂钩链的开头调用过程。 链中的每个挂钩过程确定是否将事件传递给下一个过程。 挂钩过程通过调用 CallNextHookEx 函数将事件传递给下一过程。
- 安装和释放挂钩过程
SetWindowsHookEx 传递模块句柄、指向挂钩过程入口点的指针,以及线程标识符的 0,指示挂钩过程应与调用线程位于同一桌面中的所有线程相关联。 以下示例中显示了此序列。
HOOKPROC hkprcSysMsg;
static HINSTANCE hinstDLL;
static HHOOK hhookSysMsg;
hinstDLL = LoadLibrary(TEXT("c:\\myapp\\sysmsg.dll"));
hkprcSysMsg = (HOOKPROC)GetProcAddress(hinstDLL, "SysMessageProc");
hhookSysMsg = SetWindowsHookEx(
WH_SYSMSGFILTER,
hkprcSysMsg,
hinstDLL,
0);
可以通过调用 UnhookWindowsHookEx 函数来释放特定于线程的挂钩过程, (从挂钩链) 中删除其地址,并指定要释放的挂钩过程的句柄。 一旦应用程序不再需要挂钩过程,就立即释放它。
5、代码测试
5.1 代码1
#include "pch.h"
#include <Windows.h>
#include <iostream>
std::string get_time()
{
SYSTEMTIME sys;
GetLocalTime(&sys);
char time[255] = { 0 };
sprintf_s(time, "[%4d/%02d/%02d %02d:%02d:%02d]", sys.wYear, sys.wMonth, sys.wDay, sys.wHour, sys.wMinute, sys.wSecond);
return std::string(time);
}
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
/*
typedef struct tagKBDLLHOOKSTRUCT {
DWORD vkCode; // 按键代号
DWORD scanCode; // 硬件扫描代号,同 vkCode 也可以作为按键的代号。
DWORD flags; // 事件类型,一般按键按下为 0 抬起为 128。
DWORD time; // 消息时间戳
ULONG_PTR dwExtraInfo; // 消息附加信息,一般为 0。
}KBDLLHOOKSTRUCT,*LPKBDLLHOOKSTRUCT,*PKBDLLHOOKSTRUCT;
*/
KBDLLHOOKSTRUCT* ks = (KBDLLHOOKSTRUCT*)lParam; // 包含低级键盘输入事件信息
char buffer[255];
DWORD code = ks->vkCode;
std::string t = get_time();
char state[20];
if (wParam == WM_KEYDOWN)
{
strcpy_s(state, "按下");
}
else if (wParam == WM_KEYUP)
{
strcpy_s(state, "抬起");
}
sprintf_s(buffer, "[键盘]%s 键代码:%d %s", t.c_str(), code, state);
std::cout << buffer << std::endl;
//return 1; // 拦截消息
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam)
{
/*
typedef struct tagMOUSEHOOKSTRUCT {
POINT pt; // Point数据
HWND hwnd; // 接收鼠标消息的窗体的句柄
UINT wHitTestCode; // 指定点击测试值
ULONG_PTR dwExtraInfo; // 指定和该消息相关联的附加信息。
} MOUSEHOOKSTRUCT, FAR* LPMOUSEHOOKSTRUCT, * PMOUSEHOOKSTRUCT;
*/
MOUSEHOOKSTRUCT* ms = (MOUSEHOOKSTRUCT*)lParam;
POINT pt = ms->pt;
std::string time = get_time();
char buffer[1024];
char state[20] = "未知";
if (wParam == WM_LBUTTONDOWN)
{
strcpy_s(state, "左键按下");
}
else if (wParam == WM_LBUTTONUP)
{
strcpy_s(state, "左键抬起");
}
else if (wParam == WM_RBUTTONDOWN)
{
strcpy_s(state, "右键按下");
}
else if (wParam == WM_RBUTTONUP)
{
strcpy_s(state, "右键抬起");
}
else if (wParam == WM_MOUSEMOVE)
{
strcpy_s(state, "移动");
}
sprintf_s(buffer, "[鼠标]%s 键代码:x:%d y:%d %s", time.c_str(), pt.x, pt.y, state);
std::cout << buffer << std::endl;
//return 1; // 拦截消息
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
int main()
{
HINSTANCE hM = GetModuleHandle(NULL), hK = GetModuleHandle(NULL);
HHOOK g_Hook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardProc, hK, 0);
HHOOK g_Hook2 = SetWindowsHookEx(WH_MOUSE_LL, MouseProc, hM, 0);
// 消息循环
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
UnhookWindowsHookEx(g_Hook);
return 0;
}
结语
如果您觉得该方法或代码有一点点用处,可以给作者点个赞,或打赏杯咖啡;
╮( ̄▽ ̄)╭
如果您感觉方法或代码不咋地
//(ㄒoㄒ)//,就在评论处留言,作者继续改进;
o_O???
如果您需要相关功能的代码定制化开发,可以留言私信作者;
(✿◡‿◡)
感谢各位大佬童鞋们的支持!
( ´ ▽´ )ノ ( ´ ▽´)っ!!!