You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

使用WinAPI WH_CALLWNDPROC钩子时回调函数未调用的技术问题咨询

解决WH_CALLWNDPROC钩子无法触发回调的问题

首先得明确一个核心知识点:WH_CALLWNDPROC钩子的回调函数必须运行在目标线程的地址空间中。你之前用本地回调(控制台程序里的CallWndProc)能成功触发WH_MOUSE/WH_KEYBOARD,是因为这两类钩子的线程钩子在跨进程场景下有特殊处理,但WH_CALLWNDPROC不行——目标进程(记事本)根本无法访问你控制台程序里的函数内存,自然不会触发回调。

你之前尝试过DLL但无效,大概率是DLL的实现有问题,比如导出函数名字被修饰、回调输出方式不对,或者钩子句柄管理有误。下面是具体的解决步骤和修正代码:

1. 编写正确的钩子DLL

首先需要把回调函数放在DLL中,并且确保函数能被正确导出,同时用适合跨进程调试的输出方式:

winhooks_dll.cpp

#include <Windows.h>
#include <cstdio>

// 每个进程加载DLL时都会拥有自己的这个全局变量副本
HHOOK g_hook = nullptr;

// 用extern "C"避免C++名字修饰,确保GetProcAddress能找到
extern "C" __declspec(dllexport) LRESULT CALLBACK CallWndProcHook(int nCode, WPARAM wParam, LPARAM lParam) {
    if (nCode >= 0) {
        // 解析WH_CALLWNDPROC的参数
        CWPSTRUCT* pMsg = reinterpret_cast<CWPSTRUCT*>(lParam);
        // 用OutputDebugString输出,配合DebugView工具查看(记事本没有控制台,cout看不到)
        wchar_t debugMsg[256];
        swprintf_s(debugMsg, L"[Hook] Received message: 0x%X (HWND: 0x%p)\n", pMsg->message, pMsg->hwnd);
        OutputDebugStringW(debugMsg);
    }
    // 必须调用下一个钩子,否则会破坏钩子链
    return CallNextHookEx(g_hook, nCode, wParam, lParam);
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
    switch (ul_reason_for_call) {
    case DLL_PROCESS_ATTACH:
        // 禁用DLL_THREAD_ATTACH/DETACH通知提升性能
        DisableThreadLibraryCalls(hModule);
        break;
    case DLL_PROCESS_DETACH:
        // 进程卸载DLL时自动卸载钩子
        if (g_hook) {
            UnhookWindowsHookEx(g_hook);
            g_hook = nullptr;
        }
        break;
    }
    return TRUE;
}

配套的.def文件(winhooks_dll.def)

为了彻底避免名字修饰问题,建议添加这个.def文件:

LIBRARY winhooks_dll
EXPORTS
CallWndProcHook @1

2. 修正控制台程序代码

现在修改你的控制台程序,正确加载DLL并获取导出的回调函数:

#include <Windows.h>
#include <iostream>

HHOOK _hook;
HINSTANCE _dllInstance;
// 定义钩子回调的函数类型
typedef LRESULT(CALLBACK* HOOKPROC_CWND)(int, WPARAM, LPARAM);
HOOKPROC_CWND _dllCallback;

int InstallHook(const wchar_t* title) {
    std::wcout << L"INFO: Getting window handle for \"" << title << L"\"...\n";
    HWND hwnd = FindWindow(NULL, title);
    if (hwnd == NULL) {
        std::cout << "ERROR: Could not find target window. Last error: " << GetLastError() << "\n";
        return -1;
    }

    std::wcout << L"INFO: Getting ThreadID (TID) of \"" << title << L"\"...\n";
    DWORD pid = 0;
    DWORD tid = GetWindowThreadProcessId(hwnd, &pid);
    if (tid == 0) {
        std::cout << "ERROR: Could not get target thread ID. Last error: " << GetLastError() << "\n";
        return -2;
    }

    std::wcout << L"INFO: Loading the DLL\n";
    _dllInstance = LoadLibrary(L"winhooks_dll.dll");
    if (_dllInstance == NULL) {
        std::cout << "ERROR: Could not load DLL. Last error: " << GetLastError() << "\n";
        return -3;
    }

    std::wcout << L"INFO: Getting callback function from DLL...\n";
    _dllCallback = reinterpret_cast<HOOKPROC_CWND>(GetProcAddress(_dllInstance, "CallWndProcHook"));
    if (_dllCallback == NULL) {
        std::cout << "ERROR: Could not get callback function. Last error: " << GetLastError() << "\n";
        FreeLibrary(_dllInstance);
        return -4;
    }

    std::wcout << L"INFO: Setting WH_CALLWNDPROC hook...\n";
    _hook = SetWindowsHookEx(WH_CALLWNDPROC, _dllCallback, _dllInstance, tid);
    if (_hook == NULL) {
        std::cout << "ERROR: Could not set hook. Last error: " << GetLastError() << "\n";
        FreeLibrary(_dllInstance);
        return -5;
    }

    return 0;
}

int main() {
    const wchar_t* title = L"Untitled - Notepad";
    int result = InstallHook(title);
    if (result != 0) {
        std::wcout << L"ERROR: Hook installation failed with code " << result << L"\n";
        system("pause");
        return 1;
    }

    std::wcout << L"SUCCESS: Hook installed on " << title << L"\n";
    std::wcout << L"Running message loop... Use DebugView to see hook output.\n";

    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    // 程序退出时清理资源
    if (_hook) UnhookWindowsHookEx(_hook);
    if (_dllInstance) FreeLibrary(_dllInstance);

    return 0;
}

3. 关键注意事项

  • 位数匹配:确保你的控制台程序和DLL都是32位或64位(比如记事本是64位,你的程序也必须编译成64位),否则LoadLibrary会失败。
  • 调试工具:因为记事本没有控制台窗口,所以用OutputDebugString输出的内容需要用微软的DebugView工具查看。
  • 钩子链完整性:回调函数中必须调用CallNextHookEx,否则会影响其他钩子的正常工作。
  • DLL卸载:在DLL的DLL_PROCESS_DETACH中卸载钩子,避免目标进程残留无效钩子。

内容的提问来源于stack exchange,提问作者Nur1

火山引擎 最新活动