c++ - 如何从 Win32 中的挂钩 DLL 记录/输出消息?
问题描述
我有一个 Win32 GUI 应用程序,其中一个线程在按下按钮时启动,如下所示:
...
switch (wmId)
{
case int(BTN::Test) :
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)dllTest, &hWnd, 0, NULL);
...
HHOOK hhookSysMsg = nullptr;
HINSTANCE hinstDLL = nullptr;
void dllTest() {
HOOKPROC hkprcSysMsg;
hinstDLL = LoadLibraryA("F:\\projects\\_dll_hook\\x64\\Release\\hook.dll");
if (hinstDLL == NULL) {
prnt("Error loading dll: #%d", PV, GetLastError());
return;
}
hkprcSysMsg = (HOOKPROC)GetProcAddress(hinstDLL, "SysMessageProc");
if (hkprcSysMsg == NULL) {
prnt("Error getting address of dll.SysMessageProc: #%d", PV, GetLastError());
return;
}
hhookSysMsg = SetWindowsHookExA(
WH_CALLWNDPROC,
hkprcSysMsg,
hinstDLL,
0);
if (hhookSysMsg == NULL) {
prnt("Error hooking dll.SysMessageProc: #%d", PV, GetLastError());
return;
}
Sleep(5000);
UnhookWindowsHookEx(hhookSysMsg);
FreeLibrary(hinstDLL);
}
这是DLL代码:
std::unordered_map<int, std::string> map;
void loadMsg() {
std::fstream stream("F:\\tmp\\_dll\\msg.txt");
if (stream.is_open()) {
std::string line;
while (std::getline(stream, line)) {
auto index = line.find(' ');
if (index != std::string::npos)
map.insert(std::make_pair(std::stoi(line.substr(0, index)), line.substr(index + 1)));
}
stream.close();
}
}
INT APIENTRY DllMain(HMODULE hDLL, DWORD Reason, LPVOID Reserved) {
switch (Reason) {
case DLL_PROCESS_ATTACH:
OutputDebugStringA("DLL attach function called");
loadMsg();
break;
case DLL_PROCESS_DETACH:
OutputDebugStringA("DLL detach function called");
break;
case DLL_THREAD_ATTACH:
OutputDebugStringA("DLL thread attach function called");
break;
case DLL_THREAD_DETACH:
OutputDebugStringA("DLL thread detach function called");
break;
}
return TRUE;
}
extern "C" __declspec(dllexport) auto SysMessageProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode < 0) return CallNextHookEx(NULL, nCode, wParam, lParam);
if (nCode == HC_ACTION) {
CWPSTRUCT* pCWP = (CWPSTRUCT*)lParam;
HWND hWnd = pCWP->hwnd;
char wclass[256]; wclass[0] = 0;
if (GetClassNameA(hWnd, wclass, 255) != 0) {
std::string msg = map[pCWP->message];
if (1 || msg == "WM_NOTIFY") {
char out[256]; out[0] = 0;
sprintf_s(out, 255, "class: '%s', msg: %s %08X\n", wclass, msg.c_str(), pCWP->message);
OutputDebugStringA(out);
}
}
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
此代码有效,但它永远不会从其他程序(如记事本)打印出(在 DebugView 或 MSVS 调试输出中)消息。
如果我将 OutputDebugStringA 替换为文件的输出,目标程序和 explorer.exe 经常会崩溃或程序冻结。但是,如果我查看该文件,我可以看到来自不同程序的消息被捕获在那里。
那么在这种情况下显示消息的正确方法是什么,它可以快速并从其他程序捕获消息,就像在 Spy++ 中所做的那样?
解决方案
您的函数与其线程回调的预期dllTest()
签名不匹配。因此,您的代码甚至在触及 DLL 之前就具有未定义的行为。CreateThread()
摆脱(LPTHREAD_START_ROUTINE)
类型转换,您将看到编译器抱怨不匹配。正确的声明是:
DWORD WINAPI dllTest(LPVOID)
您的 DLLSysMessageProc()
函数声明不正确。也。所以这是未定义行为的另一个来源。正确的声明是:
extern "C" __declspec(dllexport) LRESULT CALLBACK SysMessageProc(int nCode, WPARAM wParam, LPARAM lParam)
此外,没有理由转换pCWP->message
为 astd::string
只是为了比较它的值。您可以按原样进行比较:
if (1 || pCWP->message == WM_NOTIFY)
推荐阅读
- java - 如何在 graphql-java-tools Resolver 中 @Autowire 一个 JPA 存储库
- java - Java:无限循环 hasNextInt()
- python - pd.read_sql 方法来计算大型 Access 数据库中的行数
- c++ - 为什么 .idl 文件中使用的 `cpp_quote` 和 `pragma midl_echo` 不向 C++/WinRT 项目中生成的标头输出任何内容?
- xamarin.forms - 跨平台 Xamarin 表单应用程序 - 未显示 Android 启动画面
- javascript - 打字稿更改对象属性值动态错误
- flutter - 如何在 Flutter 的 Column 中使用带有 ListView 的扩展磁贴
- java - ArrayList.removeAll(Collection<>) 是否关心重复项?
- r - R:如何在测试结果和警告时从测试报告中省略测试警告消息
- javascript - appendChild 或 prepend 导致 SELECT 项目重新排序