在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即可。