在windows API中SetWindowsHookEx函数是可以用于API挂钩的,它主要用于跟踪某些类型的系统事件,它和之前那几个项目不同的是SetWindowsHookEx API不会修改函数的功能,而是执行回调函数时每当某个事件被触发时,事件类型仅限于Windows提供事件类型。
HHOOK SetWindowsHookExA([in] int idHook,[in] HOOKPROC lpfn,[in] HINSTANCE hmod,[in] DWORD dwThreadId);
这里的第一个参数表示要安装的挂钩过程的类型。这里微软提供了很多值来供我们选择。
| 值 | 含义 |
|---|---|
| WH_CALLWNDPROC4 | 安装一个挂钩过程,用于在系统将消息发送到目标窗口过程之前监视消息。有关详细信息,请参阅 CallWindowProcW 函数/CallWindowProcA 函数 挂钩过程。 |
| WH_CALLWNDPROCRET12 | 安装一个挂钩过程,该过程在目标窗口过程处理消息后监视消息。有关详细信息,请参阅 [HOOKPROC 回调函数] (nc-winuser-hookproc.md) 挂钩过程。 |
| WH_CBT5 | 安装用于接收对 CBT 应用程序有用的通知的挂钩过程。有关详细信息,请参阅 CBTProc 挂钩过程。 |
| WH_DEBUG9 | 安装可用于调试其他挂钩过程的挂钩过程。有关详细信息,请参阅 DebugProc 挂钩过程。 |
| WH_FOREGROUNDIDLE11 | 安装将在应用程序的前台线程变为空闲状态时调用的挂钩过程。此挂钩可用于在空闲时间执行低优先级任务。有关详细信息,请参阅 ForegroundIdleProc 挂钩过程。 |
| WH_GETMESSAGE3 | 安装用于监视发布到消息队列的消息的挂钩过程。有关详细信息,请参阅 GetMsgProc 挂钩过程。 |
| WH_JOURNALPLAYBACK1 | 警告Windows 11及更新版本:不支持日记挂钩 API。建议改用 SendInput TextInput API。安装一个挂钩过程,该过程发布以前由 WH_JOURNALRECORD 挂钩过程记录的消息。有关详细信息,请参阅 JournalPlaybackProc 挂钩过程。 |
| WH_JOURNALRECORD0 | 警告Windows 11及更新版本:不支持日记挂钩 API。建议改用 SendInput TextInput API。安装一个挂钩过程,用于记录发布到系统消息队列的输入消息。此挂钩可用于记录宏。有关详细信息,请参阅 JournalRecordProc 挂钩过程。 |
| WH_KEYBOARD2 | 安装用于监视击键消息的挂钩过程。有关详细信息,请参阅 KeyboardProc 挂钩过程。 |
| WH_KEYBOARD_LL13 | 安装用于监视低级别键盘输入事件的挂钩过程。有关详细信息,请参阅 [LowLevelKeyboardProc] (/windows/win32/winmsg/lowlevelkeyboardproc) 挂钩过程。 |
| WH_MOUSE7 | 安装监视鼠标消息的挂钩过程。有关详细信息,请参阅 MouseProc 挂钩过程。 |
| WH_MOUSE_LL14 | 安装用于监视低级别鼠标输入事件的挂钩过程。有关详细信息,请参阅 LowLevelMouseProc 挂钩过程。 |
| WH_MSGFILTER-1 | 安装挂钩过程,用于监视由于对话框、消息框、菜单或滚动条中的输入事件而生成的消息。有关详细信息,请参阅 MessageProc 挂钩过程。 |
| WH_SHELL10 | 安装一个挂钩过程,用于接收对 shell 应用程序有用的通知。有关详细信息,请参阅 ShellProc 挂钩过程。 |
| WH_SYSMSGFILTER6 | 安装挂钩过程,用于监视由于对话框、消息框、菜单或滚动条中的输入事件而生成的消息。挂钩过程监视与调用线程位于同一桌面中的所有应用程序的消息。有关详细信息,请参阅 SysMsgProc 挂钩过程。 |
这里其实说的简单点就是你要监视的事件类型,比如WH_KEYBOARD_LL标志用于监视可充当键盘记录器的消息,同样也可以设置WH_MOUSE_LL标志来监视鼠标的单击。
lpfn参数表示当指定事件发生时回调函数的指针,在这种情况下,只要单机鼠标这个函数就会执行。
lpfn需要的是回调函数,所以回调函数的类型应该是HOOKPRO。
如下:
HOOKPROC Hookproc;LRESULT Hookproc(int code,[in] WPARAM wParam,[in] LPARAM lParam)
所以我们应该这样去定义函数:
LRESULT HookTest(int nCode,WPARAM wParam,LPARAM lParam) {//...}
这里回调函数还需要使用CallNextHookEx Windows API返回并输出,CallNextHookEx 将钩子信息传递给钩子链中的下一个钩子过程,说白了就是在下次执行时将钩子的信息传递给回调函数。
如下代码:
#include <iostream>#include <Windows.h>LRESULT HookTest(int nCode,WPARAM wParam,LPARAM lParam) {//...return CallNextHookEx(NULL,nCode,wParam,lParam);}int main(){}
接下来就是回调函数的代码了,这个代码我们监视单机了那个鼠标按钮。
#include <iostream>#include <Windows.h>LRESULT HookTest(int nCode,WPARAM wParam,LPARAM lParam) {//...if (wParam == WM_LBUTTONDOWN) {printf("鼠标左键点击!!!");}if (wParam == WM_RBUTTONDOWN) {printf("鼠标右键点击!!!");}if (wParam == WM_MBUTTONDOWN) {printf("鼠标中键点击");}return CallNextHookEx(NULL,nCode,wParam,lParam);}int main(){}
获取到监视用户鼠标点击之后,下一步是确保保持挂钩的过程,这里需要通过特定时间段内执行监控代码来实现,许哦一我们需要在线程内调用SetWindowsHookExW,并使用WatForSingleObject Win API在所需的时间内保持活动状态。
#include <iostream>#include <Windows.h>LRESULT HookTest(int nCode,WPARAM wParam,LPARAM lParam) {//...if (wParam == WM_LBUTTONDOWN) {MessageBoxA(0, "我点击了左键", 0, 0);}if (wParam == WM_RBUTTONDOWN) {MessageBoxA(0, "我点击了右键", 0, 0);}if (wParam == WM_MBUTTONDOWN) {MessageBoxA(0, 0, 0, 0);}return CallNextHookEx(NULL,nCode,wParam,lParam);}BOOL MouseClieckTest() {MSG Msg = { 0 };HHOOK hMouseHook = SetWindowsHookExW(WH_MOUSE_LL, (HOOKPROC)HookTest, NULL, NULL);if (!hMouseHook) {return FALSE;}while (GetMessageW(&Msg,NULL,NULL,NULL)) {DefWindowProcW(Msg.hwnd,Msg.message,Msg.wParam,Msg.lParam);}return TRUE;}int main(){EnumChildWindows(NULL, (WNDENUMPROC)MouseClieckTest, NULL);getchar();return 0;}
这里需要唯一的解释的是如下代码:
while (GetMessageW(&Msg,NULL,NULL,NULL)) {DefWindowProcW(Msg.hwnd,Msg.message,Msg.wParam,Msg.lParam);}return TRUE;
这里是需要通过DefWindowProcW来处理所有消息事件,DefWindowProcW调用默认窗口过程为应用程序不处理的任何窗口消息提供默认处理。
如果需要获取到详细信息,GetMessageW必须首先调用,他会从调用线程中检索信息,然后将信息传递给DefWindowProcW,最后将其处理。
取消挂钩的话删除SetWindowsHookEx函数安装的任何挂钩,所以需要调用UnHookWindowsHookEx API即可。