永远不会调用全局CBT钩子

时间:2014-10-28 15:18:52

标签: c++ winapi dll windows-7 hook

我试图让应用程序了解系统范围内顶级窗口的创建和销毁情况。我已经制作了一个利用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;
}

1 个答案:

答案 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()更灵活的过滤功能