You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Windows下如何拦截DLL函数调用?实现测量值实时监控

问题背景

我们的C++应用运行在Windows系统上,由调用Library.dll的Program.exe组成。Library.dll中实现了如下方法:

double SomeClass::getMeasurement() {...}

其声明如下:

class SomeClass {
    public:
    __declspec(dllexport) double getMeasurement(); 
};

Program.exe每秒调用该方法一次,并将结果写入文件,但输出未实时刷新。现有使用老版本软件(方法签名未变更)的客户需要实时监控测量值。

疑问:是否可以创建仅含如下方法的Spy.dll:

double SomeClass::getMeasurement() {
    double value;
    // 如何从Library.dll的SomeClass::getMeasurement()获取value
    std::string message =  std::string("measurement: ") + std::to_string(value);
    OutputDebugStringA(msg.c_str());
    return value;  
}

并让Windows优先加载该DLL的符号而非Library.dll?


可行方案说明

完全可以通过这种方式实现实时监控,核心是利用Windows的DLL加载优先级机制,让Spy.dll替代原Library.dll被加载,同时在Spy.dll中调用原DLL的真实方法。具体步骤和注意事项如下:

1. 正确实现Spy.dll的代理逻辑

你需要修改Spy.dll代码,确保它能正确调用原Library.dll的方法:

  • 先将原Library.dll重命名(比如改为OriginalLibrary.dll),把你的Spy.dll命名为Library.dll,这样程序会优先加载你的DLL。
  • 在Spy.dll中,通过LoadLibrary加载重命名后的原DLL,再用GetProcAddress获取原方法的地址。注意:类成员函数的导出会被C++名称修饰,需要通过dumpbin /exports Library.dll查看原DLL的导出表,拿到真实的导出修饰名。

修改后的示例代码:

#include <windows.h>
#include <string>

// 定义原成员函数的指针类型(__thiscall是类成员函数的默认调用约定)
typedef double (__thiscall* GetMeasurementFunc)(void*);

HMODULE hOriginalLib = nullptr;
GetMeasurementFunc pOriginalGetMeasurement = nullptr;

// DLL加载时初始化
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
    switch (ul_reason_for_call) {
        case DLL_PROCESS_ATTACH:
            // 加载重命名后的原DLL
            hOriginalLib = LoadLibraryA("OriginalLibrary.dll");
            if (hOriginalLib) {
                // 替换成原DLL中实际的导出修饰名,比如"?getMeasurement@SomeClass@@QAENXZ"
                pOriginalGetMeasurement = (GetMeasurementFunc)GetProcAddress(hOriginalLib, "?getMeasurement@SomeClass@@QAENXZ");
            }
            break;
        case DLL_PROCESS_DETACH:
            if (hOriginalLib) {
                FreeLibrary(hOriginalLib);
            }
            break;
    }
    return TRUE;
}

// 导出与原函数修饰名一致的代理函数
extern "C" __declspec(dllexport) double __thiscall SomeClass_getMeasurement(void* thisPtr) {
    double value = 0.0;
    if (pOriginalGetMeasurement) {
        // 调用原DLL的方法
        value = pOriginalGetMeasurement(thisPtr);
        // 输出实时测量值到调试通道
        std::string message = "measurement: " + std::to_string(value);
        OutputDebugStringA(message.c_str());
    }
    return value;
}

2. 利用Windows DLL加载优先级实现替换

Windows加载DLL的优先级顺序(从高到低):

  • 程序当前目录
  • 系统目录(System32)
  • 环境变量PATH中的目录

只需将重命名后的原Library.dll(如OriginalLibrary.dll)和你的Spy.dll(命名为Library.dll)放在Program.exe的同一目录下,程序启动时就会优先加载你的Spy.dll。

3. 关键注意事项

  • 导出修饰名必须完全匹配:Spy.dll导出的函数修饰名要和原Library.dll完全一致,否则程序会因找不到函数启动失败。
  • 原DLL路径要正确:在Spy.dll中加载原DLL时,最好将两者放在同一目录,避免路径查找错误。
  • 兼容性保障:因为方法签名未变更,这种方式对老版本软件完全兼容,无需修改Program.exe的任何代码。
  • 调试信息查看:客户可以使用DebugView工具捕获OutputDebugStringA输出的实时测量值。

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 12:52:41