【问题标题】:Global CBT hook is never called从未调用全局 CBT 挂钩
【发布时间】:2014-12-24 01:45:16
【问题描述】:

我正在尝试制作一个应用程序,它可以在系统范围内获取有关创建和销毁顶级窗口的通知。我编写了一个代码来利用 CBT 钩子。该解决方案包含两个项目,DLL 和 EXE。 EXE 项目有对 DLL 项目的引用。钩子是从 DLL 设置的。 EXE 项目中有一个消息循环。问题是 CBT 挂钩不起作用。在 VS 调试器的帮助下,我发现钩子 callback 永远不会被调用,而来自 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;
}

【问题讨论】:

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


    【解决方案1】:

    如果您在 64 位 Windows 上运行,则需要 32 位和 64 位版本的 DLL 才能挂钩每个正在运行的进程。 32 位 DLL 不能挂钩 64 位进程,反之亦然。所以需要在32位进程中调用SetWindowsHookEx()来hook 32位进程,从64位进程中调用64位进程。

    更重要的是,您的 DLL 的一个单独实例被注入到每个正在运行的进程中,因此您的 fpDecoder 回调指针在 DLL 的每个实例中都是 NULL,除了您的 EXE 正在调用 InstallHook() 的那个实例。所以你需要重新设计你的钩子以使用进程间通信(窗口消息、命名管道、邮槽、套接字等)与你的主 EXE 通信,你不能使用函数指针。

    根据您实际调试的进程,您可能看不到 cbtProc() 被调用。如果您正在调试您的主 EXE 进程,那么一旦安装了钩子,您的代码就不会在您的 EXE 进程中触发任何 CBT 活动,并且调试器不会向您显示在它未调试的其他进程中发生的任何 CBT 活动.

    根据您在钩子中实际寻找的内容,您可以考虑改用SetWinEventHook(),因为它可以使用或不使用DLL,并且它具有比SetWindowsHookEx() 更灵活的过滤功能。

    【讨论】:

    • 我正在运行 32 位 W7。该应用程序编译为 32 位。虽然关于指针的事情对我来说是新事物。
    • 我也在使用SetWinEventHook 来表示“前景窗口已更改”消息。那里没有“已创建窗口”消息。
    • 另外,您没有回答主要问题:为什么从未调用过cbtProc
    • @polkovnikov.ph: SetWinEventHook() 使用EVENT_OBJECT_CREATE 事件报告窗口创建。是的,我确实回答了这个问题,实际上 - 回调没有被调用,因为 fpDecoder 在每个 DLL 实例中都是 NULL,而不是主 EXE 进程中的实例。
    • fpDecodercbtProc 是不同的功能。调试器显示cbtProc 没有被调用,而不是fpDecodernullEVENT_OBJECT_CREATE 报告所有类型的对象,不仅是窗口,我不喜欢那样。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多