这是一段可以实现监控Windows事件的dll代码,使用VS2022选择C++ 应用程序扩展创建之后只需要修改 dllmain.cpp 的代码即可,无需其它任何配置,使用 release x64 build之后就可以被其它应用程序调用
#include "pch.h"
#include <windows.h>
#include <string>
//基础C++的dll代码框架
// 共享内存区定义
//将 g_hMainWnd 和 g_hEventHook 放在共享内存区
//RWS 标志表示该段具有 Read/Write/Shared 属性,确保多进程可访问同一内存区域
//g_hMainWnd 是主窗口的句柄,用于接收事件通知
//g_hEventHook 是事件钩子的句柄,用于监听窗口事件
#pragma data_seg(".SHARED")
HWND g_hMainWnd = NULL;
HWINEVENTHOOK g_hEventHook = NULL;
#pragma data_seg()
#pragma comment(linker, "/SECTION:.SHARED,RWS")
//事件处理函数,是一个回调函数,在事件发生时被调用,需要导出为C风格的函数
//参数说明:
// hWinEventHook: 事件钩子的句柄
// event: 事件类型
// hwnd: 事件发生的窗口句柄
// idObject: 事件对象的ID
// idChild: 事件子对象的ID
// dwEventThread: 事件发生的线程ID
// dwmsEventTime: 事件发生的时间戳
extern "C" __declspec(dllexport)
void CALLBACK WinEventProc(
HWINEVENTHOOK hWinEventHook,
DWORD event,
HWND hwnd,
LONG idObject,
LONG idChild,
DWORD dwEventThread,
DWORD dwmsEventTime)
{
//IsWindow是一个API函数,用于检查指定的窗口句柄是否有效,必须在调用startMonitoring函数中传递窗口句柄,否则就直接返回
if (!IsWindow(g_hMainWnd)) {
return;
}
//如果没有标题就直接退出...
wchar_t buf[256] = { 0 };
if (!GetWindowTextW(hwnd, buf, _countof(buf))) {
return;
}
//这里创建一个 COPYDATASTRUCT 结构体,用于发送窗口标题到主程序
//这个结构体包含三个成员:
// dwData: 用于标识数据类型,这里使用事件类型
// cbData: 数据的大小,这里使用窗口标题的长度
// lpData: 指向数据的指针,这里使用窗口标题的缓冲区
COPYDATASTRUCT cds = { 0 };
cds.dwData = event;
cds.cbData = (wcslen(buf) + 1) * sizeof(wchar_t);
cds.lpData = buf;
// 调试输出标题(可选)
//OutputDebugStringW(L"窗口标题: ");
//OutputDebugStringW(buf);
//发送窗口标题到主程序(有些事件并没有)
if (!SendMessageW(g_hMainWnd, WM_COPYDATA, 0, (LPARAM)&cds)) {
DWORD err = GetLastError();
wchar_t msg[64];
swprintf(msg, _countof(msg), L"消息发送失败: %d", err);
//OutputDebugStringW(msg);
}
}
//导出函数,用于开始监控窗口事件
//参数说明:
// hCallbackWnd: 主窗口的句柄,用于接收事件通知,如果不需要接收事件通知,可以传NULL
//将 hcallbackWnd 存储在共享内存区 g_hMainWnd 中,用于在事件处理函数中发送消息到主窗口
//setWinEventHook 函数用于设置事件钩子,监听指定范围的窗口事件,在这里监听所有事件,WINEVENT_OUTOFCONTEXT 标志表示事件处理函数在非钩子线程中调用,
//而是位于独立线程中,被系统自行调度使用,无需注入到目标进程中.这是区别于消息hook的地方,消息钩子需要注入到目标进程中,而事件钩子不需要.
//setwineventhook 函数的参数说明:
// eventMin: 监听的最小事件类型,这里设置为 EVENT_MIN,表示监听所有事件
// eventMax: 监听的最大事件类型,这里设置为 EVENT_MAX,表示监听所有事件
// hmodWinEventProc: 事件处理函数所在的模块句柄,这里设置为 NULL,表示使用当前进程的模块
// pfnWinEventProc: 事件处理函数的指针,这里设置为 WinEventProc,表示使用上面定义的事件处理函数
// idProcess: 监听的进程ID,这里设置为 0,表示监听所有进程
// idThread: 监听的线程ID,这里设置为 0,表示监听所有线程
// dwFlags: 监听标志,这里设置为 WINEVENT_OUTOFCONTEXT,表示事件处理函数在非钩子线程中调用
extern "C" __declspec(dllexport)
BOOL APIENTRY StartMonitoring(HWND hCallbackWnd) {
g_hMainWnd = hCallbackWnd;
g_hEventHook = SetWinEventHook(
EVENT_MIN, EVENT_MAX,
NULL, WinEventProc,
0, 0,
WINEVENT_OUTOFCONTEXT);
return g_hEventHook != NULL;
}
//导出函数,用于停止监控窗口事件
//该函数会取消之前设置的事件钩子,释放资源
//unhookwinevent 函数用于取消事件钩子,释放资源
extern "C" __declspec(dllexport)
BOOL APIENTRY StopMonitoring() {
return UnhookWinEvent(g_hEventHook);
}
// DLL入口点函数,用于处理DLL的加载和卸载事件
// 当DLL被加载或卸载时,系统会调用这个函数
// 参数说明:
// hModule: DLL模块的句柄
// ul_reason: 卸载原因,可以是 DLL_PROCESS_ATTACH, DLL_THREAD_ATTACH, DLL_THREAD_DETACH, DLL_PROCESS_DETACH
// lpReserved: 保留参数,通常为NULL
// 在这里处理 DLL_PROCESS_DETACH 事件,即当DLL被卸载时,取消事件钩子
//dll卸载过程是在所有线程都结束后,才会调用 DllMain 函数,因此可以安全地取消事件钩子
// 其他事件可以忽略,因为我们只关心 DLL_PROCESS_DETACH 事件
//返回值是固定的 TRUE,表示 DLL 的入口点函数执行成功
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason, LPVOID lpReserved) {
switch (ul_reason) {
case DLL_PROCESS_DETACH:
if (g_hEventHook) UnhookWinEvent(g_hEventHook);
break;
}
return TRUE;
}