我试图让应用程序了解系统范围内顶级窗口的创建和销毁情况。我已经制作了一个利用CBT钩子的代码。该解决方案包含两个项目,DLL和EXE。 EXE项目有一个DLL项目的引用。钩子是从DLL设置的。 EXE项目中有一个消息循环。问题是CBT钩子不起作用。在VS调试器的帮助下,我发现钩子回调从不被称为,而来自SetWindowsHookEx
的返回码非零,暗示钩子已设置。什么是错误?我该如何解决?
这是一个极小的例子。 DLL,main.cpp:
#include <windows.h>
typedef void(*DECODERPROC)(int code, WPARAM wParam, LPARAM lParam);
HINSTANCE hInst = nullptr;
HHOOK hHook = nullptr;
DECODERPROC fpDecoder = nullptr;
LRESULT CALLBACK cbtProc(int code, WPARAM wParam, LPARAM lParam) {
// FIXME: never called
if (code > 0 && fpDecoder) {
fpDecoder(code, wParam, lParam);
}
return CallNextHookEx(hHook, code, wParam, lParam);
}
__declspec(dllexport) bool InstallHook(DECODERPROC decoder) {
if (hHook) return false;
fpDecoder = decoder;
return (hHook = SetWindowsHookEx(WH_CBT, cbtProc, hInst, 0)) != NULL;
}
__declspec(dllexport) bool UninstallHook() {
if (!hHook) return false;
bool res = UnhookWindowsHookEx(hHook) != NULL;
if (res) hHook = NULL;
return res;
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
hInst = reinterpret_cast<HINSTANCE>(hModule);
return TRUE;
}
EXE,main.cpp
#include <iostream>
#include <locale>
#include <windows.h>
#include <fcntl.h>
#include <io.h>
using namespace std;
typedef void(*DECODERPROC)(int code, WPARAM wParam, LPARAM lParam);
__declspec(dllimport) bool InstallHook(DECODERPROC);
__declspec(dllimport) bool UninstallHook();
int main() {
_setmode(_fileno(stdout), _O_U8TEXT);
WNDCLASS windowClass = {};
windowClass.lpfnWndProc = [](HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) -> LRESULT {
if (message == WM_DESTROY)
UninstallHook();
return DefWindowProc(hWnd, message, wParam, lParam);
};
LPCWSTR windowClassName = L"Foobar";
windowClass.lpszClassName = windowClassName;
if (!RegisterClass(&windowClass)) {
wcerr << L"Failed to register window class" << endl;
return 1;
}
HWND messageWindow = CreateWindow(windowClassName, 0, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, 0, 0);
if (!messageWindow) {
wcerr << L"Failed to create message-only window" << endl;
return 1;
}
InstallHook([](int code, WPARAM wParam, LPARAM lParam) {
wcout << L"Never called" << endl;
});
MSG msg;
while (GetMessage(&msg, 0, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
答案 0 :(得分:5)
如果您在Windows 64位上运行,则需要32位和64位版本的DLL才能挂钩每个正在运行的进程。 32位DLL无法挂钩64位进程,反之亦然。因此,您需要从32位进程调用SetWindowsHookEx()
来挂钩32位进程,并从64位进程调用挂钩64位进程。
更重要的是,DLL的一个单独实例会被注入到每个正在运行的进程中,因此除了你的EXE正在调用的那个实例之外,你的fpDecoder
回调指针在每个DLL实例中都是NULL {{1 }} 上。因此,您需要重新设计钩子以使用进程间通信(窗口消息,命名管道,邮件槽,套接字等)与主EXE进行通信,您不能使用函数指针。
根据您实际调试的进程,您可能看不到InstallHook()
被调用。如果您正在调试主EXE进程,那么一旦安装了挂钩,您的代码就无法在EXE的进程中触发任何CBT活动,并且调试器不会向您显示在其他进程中发生的任何CBT活动它不是调试。
根据您在钩子中实际寻找的内容,您可以考虑使用cbtProc()
,因为它可以与DLL一起使用,也可以不与DLL一起使用,并且它具有比SetWinEventHook()
更灵活的过滤功能