c++ - 如何在 Win32 中使用 DLL 挂钩子类化另一个应用程序控件?
问题描述
我正在尝试使此示例正常工作,其想法是将记事本编辑控件颜色更改为红色:
dll
#include "stdafx.h"
#include <string>
#include <commctrl.h>
#pragma comment( lib, "comctl32.lib")
#pragma data_seg("SHARED")
HWND hWndNotepad = nullptr;
#pragma data_seg()
#pragma comment(linker, "/section:SHARED,RWS")
HINSTANCE hInst;
HHOOK hGetMsgHook;
BOOL bSubclassed = FALSE;
#define WM_NOTEPADMESSAGE WM_USER + 10
LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK SubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
hInst = (HINSTANCE)hModule;
}
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
{
char out[64] = { 0 };
sprintf_s(out, 63, "DLL_PROCESS_DETACH, bSubclassed: %i", (int)bSubclassed);
OutputDebugStringA(out);
if (bSubclassed)
{
RemoveWindowSubclass(hWndNotepad, SubclassProc, 0);
bSubclassed = FALSE;
hWndNotepad = nullptr;
}
}
break;
}
return TRUE;
}
extern "C" __declspec(dllexport) BOOL CALLBACK SetHook(BOOL bInstall)
{
if (bInstall)
{
hWndNotepad = FindWindowA("Notepad", NULL);
if (hWndNotepad)
{
char out[256] = { 0 };
hGetMsgHook = SetWindowsHookExA(WH_GETMESSAGE, (HOOKPROC)GetMsgProc, hInst, 0);
if (hGetMsgHook)
{
DWORD dwThreadId = GetWindowThreadProcessId(hWndNotepad, NULL);
PostThreadMessageA(dwThreadId, WM_NOTEPADMESSAGE, 0, (LPARAM)hWndNotepad);
sprintf_s(out, 255, "hWndNotepad: %08X - Thread Id : %08X", (UINT)hWndNotepad, dwThreadId);
}
else {
sprintf_s(out, 255, "Error SetWindowsHookEx: %d", GetLastError());
}
OutputDebugStringA(out);
}
else
{
char out[] = "Notepad not found";
OutputDebugStringA(out);
return FALSE;
}
}
else
{
if (hGetMsgHook)
UnhookWindowsHookEx(hGetMsgHook);
}
return TRUE;
}
LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam)
{
MSG* lpMsg;
if (nCode >= 0)
{
lpMsg = (MSG*)lParam;
if (lpMsg->message == WM_NOTEPADMESSAGE)
{
char out[128] = { 0 };
sprintf_s(out, 127, "WM_NOTEPADMESSAGE - hWndNotepad : %08X", (UINT)hWndNotepad);
OutputDebugStringA(out);
bSubclassed = SetWindowSubclass((HWND)hWndNotepad, SubclassProc, 0, 0);
if (!bSubclassed) {
sprintf_s(out, 127, "Error SetWindowSubclass : %d", GetLastError());
OutputDebugStringA(out);
}
}
}
return CallNextHookEx(hGetMsgHook, nCode, wParam, lParam);
}
LRESULT CALLBACK SubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
char out[32] = { 0 };
sprintf_s(out, 31, "uMsg : %d", uMsg);
OutputDebugStringA(out);
switch (uMsg)
{
case WM_CTLCOLOREDIT:
{
SetDCBrushColor((HDC)wParam, RGB(255, 0, 0));
return (INT_PTR)GetStockObject(DC_BRUSH);
}
break;
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
主程序:
HINSTANCE hinstDLL = nullptr;
BOOL hookset = false;
typedef BOOL(*SetHookType)(BOOL);
SetHookType SetHook = nullptr;
DWORD WINAPI dllTest(LPVOID) {
char out[128] = { 0 };
sprintf_s(out, 127, "--- dllTest beg ---");
OutputDebugStringA(out);
hookset = !hookset;
if(hinstDLL == nullptr) {
#if _WIN32 || _WIN64
#if _WIN64
hinstDLL = LoadLibraryA("F:\\projects\\_dll_hook\\x64\\Release\\hook.dll");
#else
hinstDLL = LoadLibraryA("F:\\projects\\_dll_hook\\Release\\hook.dll");
#endif
#endif
sprintf_s(out, 127, "DLL loaded");
OutputDebugStringA(out);
}
if (hinstDLL == nullptr) {
sprintf_s(out, 127, "Error loading dll: #%d", GetLastError());
OutputDebugStringA(out);
return 0;
}
if (SetHook == nullptr) {
SetHook = (SetHookType)GetProcAddress(hinstDLL, "SetHook");
if (SetHook == nullptr) {
sprintf_s(out, 127, "Error getting address of dll.SetHook: #%d", GetLastError());
OutputDebugStringA(out);
return 0;
}
SetHook(hookset);
}
if (!hookset) {
FreeLibrary(hinstDLL);
hinstDLL = nullptr;
SetHook = nullptr;
sprintf_s(out, 127, "DLL freed");
OutputDebugStringA(out);
}
Sleep(10000); // <-- ONE POSSIBLE SOLUTION
sprintf_s(out, 127, "--- dllTest end ---");
OutputDebugStringA(out);
return 0;
}
调用 dllTest 两次后得到的输出:
[13800] --- dllTest beg ---
[13800] DLL loaded
[13800] hWndNotepad: 002B0FAC - Thread Id : 00003B30
[13800] --- dllTest end ---
[13800] DLL_PROCESS_DETACH, bSubclassed: 0
[10356] WM_NOTEPADMESSAGE - hWndNotepad : 002B0FAC
[10356] DLL_PROCESS_DETACH, bSubclassed: 1
[13800] --- dllTest beg ---
[13800] DLL_PROCESS_DETACH, bSubclassed: 0
[13800] DLL freed
[13800] --- dllTest end ---
看起来 WM_NOTEPADMESSAGE 已发送到记事本窗口,但未被 GetMsgProc 捕获。这段代码可能有什么问题以及如何使它工作?
谢谢!
解决方案
一、回调不起作用的原因是你的DLL会被注入到目标进程中,其他进程无法访问主程序中的函数地址。
然后,我可以重现您的问题(记事本背景无法更改)。之后主程序立即结束SetHook
,系统可能会自动注销你的钩子,然后目标进程无法访问该SubclassProc
地址。尝试在主程序中添加任何延迟,例如消息循环。
推荐阅读
- python - 用Python中另一列值的条件填充列的NA
- macos - 如何打开内部 shell 命令、二进制文件并分析它们的代码?
- machine-learning - 为什么我们需要 test_generator 和 val_generator 来进行数据增强
- java - How to check if there are 3 consecutive 1s in array in java?
- javascript - changes doesn't take effect on JSON file on heroku
- sql-server - Why does SQL Server Mgmt Studio 17.9 save MDF to C:\Users? Error: Msg 5123, Level 16, State 1, Line 1 CREATE FILE access denied
- javascript - React Filepond加载初始图像不起作用
- javafx - JavaFX:如果节点边界设置为最大值,如何拍摄节点快照?
- python - 无法让 Spark NLP 在 Databricks 上工作
- php - Postgresql 查询返回引号之间的字符串值 " 如果它包含任何空格字符,或者如果它没有空格字符则不带引号