我正在尝试创建一个应用程序来获取有关系统范围内顶级窗口的创建和销毁的信息.我已经制作了一个利用CBT钩子的代码.该解决方案包含两个项目,DLL和EXE.EXE项目有一个DLL项目的引用.钩子是从DLL设置的.EXE项目中有一个消息循环.问题是CBT钩子不起作用.在VS调试器的帮助下,我发现从不调用钩子回调,而返回代码来自SetWindowsHookEx
非零,暗示钩子已设置.怎么了?我如何解决它?
这是一个最小的例子.DLL,main.cpp:
#includetypedef 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 (hModule); return TRUE; }
EXE,main.cpp
#include#include #include #include #include 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; }
Remy Lebeau.. 5
如果您在Windows 64位上运行,则需要32位和64位版本的DLL才能挂钩每个正在运行的进程.32位DLL无法挂钩64位进程,反之亦然.因此,您需要SetWindowsHookEx()
从32位进程调用以挂接32位进程,并从64位进程调用挂钩64位进程.
更重要的是,DLL的单独实例会被注入到每个正在运行的进程中,因此fpDecoder
除了EXE正在调用的DLL之外,每个DLL实例中的回调指针都是NULL InstallHook()
.因此,您需要重新设计您的钩子以使用进程间通信(窗口消息,命名管道,邮件槽,套接字等)与您的主EXE进行通信,您不能使用函数指针.
根据您实际调试的进程,您可能看不到cbtProc()
被调用.如果您正在调试主EXE进程,那么一旦安装了挂钩,您的代码就没有做任何事情来触发EXE进程中的任何CBT活动,并且调试器不会在其他进程中显示任何未调试的CBT活动.
根据您在钩子中实际寻找的内容,您可以考虑使用SetWinEventHook()
,因为它可以与DLL一起使用,也可以不与DLL一起使用,并且它具有比以下更灵活的过滤功能SetWindowsHookEx()
.