這是一段可以實現(xiàn)監(jiān)控Windows事件的dll代碼,使用VS2022選擇C++ 應用程序擴展創(chuàng)建之后只需要修改 dllmain.cpp 的代碼即可,無需其它任何配置,使用 release x64 build之后就可以被其它應用程序調(diào)用
#include "pch.h"
#include <windows.h>
#include <string>
//基礎C++的dll代碼框架
// 共享內(nèi)存區(qū)定義
//將 g_hMainWnd 和 g_hEventHook 放在共享內(nèi)存區(qū)
//RWS 標志表示該段具有 Read/Write/Shared 屬性,確保多進程可訪問同一內(nèi)存區(qū)域
//g_hMainWnd 是主窗口的句柄,用于接收事件通知
//g_hEventHook 是事件鉤子的句柄,用于監(jiān)聽窗口事件
#pragma data_seg(".SHARED")
HWND g_hMainWnd = NULL;
HWINEVENTHOOK g_hEventHook = NULL;
#pragma data_seg()
#pragma comment(linker, "/SECTION:.SHARED,RWS")
//事件處理函數(shù),是一個回調(diào)函數(shù),在事件發(fā)生時被調(diào)用,需要導出為C風格的函數(shù)
//參數(shù)說明:
// hWinEventHook: 事件鉤子的句柄
// event: 事件類型
// hwnd: 事件發(fā)生的窗口句柄
// idObject: 事件對象的ID
// idChild: 事件子對象的ID
// dwEventThread: 事件發(fā)生的線程ID
// dwmsEventTime: 事件發(fā)生的時間戳
extern "C" __declspec(dllexport)
void CALLBACK WinEventProc(
? ? HWINEVENTHOOK hWinEventHook,
? ? DWORD event,
? ? HWND hwnd,
? ? LONG idObject,
? ? LONG idChild,
? ? DWORD dwEventThread,
? ? DWORD dwmsEventTime)
{
//IsWindow是一個API函數(shù),用于檢查指定的窗口句柄是否有效,必須在調(diào)用startMonitoring函數(shù)中傳遞窗口句柄,否則就直接返回
? ? if (!IsWindow(g_hMainWnd)) {
? ? ? ? return;
? ? }
? ? //如果沒有標題就直接退出...
? ? wchar_t buf[256] = { 0 };
? ? if (!GetWindowTextW(hwnd, buf, _countof(buf))) {
? ? ? ? return;
? ? }
//這里創(chuàng)建一個 COPYDATASTRUCT 結(jié)構體,用于發(fā)送窗口標題到主程序
//這個結(jié)構體包含三個成員:
// dwData: 用于標識數(shù)據(jù)類型,這里使用事件類型
// cbData: 數(shù)據(jù)的大小,這里使用窗口標題的長度
// lpData: 指向數(shù)據(jù)的指針,這里使用窗口標題的緩沖區(qū)
? ? COPYDATASTRUCT cds = { 0 };
? ? cds.dwData = event;
? ? cds.cbData = (wcslen(buf) + 1) * sizeof(wchar_t);
? ? cds.lpData = buf;
? ? // 調(diào)試輸出標題(可選)
? ? //OutputDebugStringW(L"窗口標題: ");
? ? //OutputDebugStringW(buf);
? ? //發(fā)送窗口標題到主程序(有些事件并沒有)
? ? if (!SendMessageW(g_hMainWnd, WM_COPYDATA, 0, (LPARAM)&cds)) {
? ? ? ? DWORD err = GetLastError();
? ? ? ? wchar_t msg[64];
? ? ? ? swprintf(msg, _countof(msg), L"消息發(fā)送失敗: %d", err);
? ? ? ? //OutputDebugStringW(msg);
? ? }
}
//導出函數(shù),用于開始監(jiān)控窗口事件
//參數(shù)說明:
// hCallbackWnd: 主窗口的句柄,用于接收事件通知,如果不需要接收事件通知,可以傳NULL
//將 hcallbackWnd 存儲在共享內(nèi)存區(qū) g_hMainWnd 中,用于在事件處理函數(shù)中發(fā)送消息到主窗口
//setWinEventHook 函數(shù)用于設置事件鉤子,監(jiān)聽指定范圍的窗口事件,在這里監(jiān)聽所有事件,WINEVENT_OUTOFCONTEXT 標志表示事件處理函數(shù)在非鉤子線程中調(diào)用,
//而是位于獨立線程中,被系統(tǒng)自行調(diào)度使用,無需注入到目標進程中.這是區(qū)別于消息hook的地方,消息鉤子需要注入到目標進程中,而事件鉤子不需要.
//setwineventhook 函數(shù)的參數(shù)說明:
// eventMin: 監(jiān)聽的最小事件類型,這里設置為 EVENT_MIN,表示監(jiān)聽所有事件
// eventMax: 監(jiān)聽的最大事件類型,這里設置為 EVENT_MAX,表示監(jiān)聽所有事件
// hmodWinEventProc: 事件處理函數(shù)所在的模塊句柄,這里設置為 NULL,表示使用當前進程的模塊
// pfnWinEventProc: 事件處理函數(shù)的指針,這里設置為 WinEventProc,表示使用上面定義的事件處理函數(shù)
// idProcess: 監(jiān)聽的進程ID,這里設置為 0,表示監(jiān)聽所有進程
// idThread: 監(jiān)聽的線程ID,這里設置為 0,表示監(jiān)聽所有線程
// dwFlags: 監(jiān)聽標志,這里設置為 WINEVENT_OUTOFCONTEXT,表示事件處理函數(shù)在非鉤子線程中調(diào)用
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;
}
//導出函數(shù),用于停止監(jiān)控窗口事件
//該函數(shù)會取消之前設置的事件鉤子,釋放資源
//unhookwinevent 函數(shù)用于取消事件鉤子,釋放資源
extern "C" __declspec(dllexport)
BOOL APIENTRY StopMonitoring() {
? ? return UnhookWinEvent(g_hEventHook);
}
// DLL入口點函數(shù),用于處理DLL的加載和卸載事件
// 當DLL被加載或卸載時,系統(tǒng)會調(diào)用這個函數(shù)
// 參數(shù)說明:
// hModule: DLL模塊的句柄
// ul_reason: 卸載原因,可以是 DLL_PROCESS_ATTACH, DLL_THREAD_ATTACH, DLL_THREAD_DETACH, DLL_PROCESS_DETACH
// lpReserved: 保留參數(shù),通常為NULL
// 在這里處理 DLL_PROCESS_DETACH 事件,即當DLL被卸載時,取消事件鉤子
//dll卸載過程是在所有線程都結(jié)束后,才會調(diào)用 DllMain 函數(shù),因此可以安全地取消事件鉤子
// 其他事件可以忽略,因為我們只關心 DLL_PROCESS_DETACH 事件
//返回值是固定的 TRUE,表示 DLL 的入口點函數(shù)執(zhí)行成功
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;
}