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

在C++中调用Delphi编写的DLL:如何正确调用函数?

问题核心

调用约定不匹配导致ESP错误,添加__stdcall后链接报错是因为生成的.lib符号与编译器期望的修饰符号不匹配。以下是两种可行的解决方法:


解决方法一:修正静态链接的符号匹配

  1. 确认DLL导出的函数符号
    打开对应项目平台(x86/x64)的VS命令提示符,运行命令查看DLL的导出符号:

    dumpbin /exports MacReadDataFile.dll
    

    记录输出中每个函数的名称和对应序号。

  2. 生成包含正确符号的.lib文件
    创建MacReadDataFile.def文件,内容如下(替换序号为dumpbin输出的实际值):

    LIBRARY MacReadDataFile.dll
    EXPORTS
    OpenDataFile @1
    LoadAndGetNextTimeData @2
    CloseDataFile @3
    

    再通过lib工具重新生成库文件:

    // 32位DLL
    lib /def:MacReadDataFile.def /out:MacReadDataFileLIB.lib /machine:x86
    // 64位DLL
    lib /def:MacReadDataFile.def /out:MacReadDataFileLIB.lib /machine:x64
    
  3. 修正代码中的函数声明
    使用__stdcall调用约定声明函数,确保与DLL匹配:

    #pragma comment(lib, "MacReadDataFileLIB.lib")
    #include <string>
    
    extern "C" int __stdcall OpenDataFile(const char*);
    extern "C" int __stdcall LoadAndGetNextTimeData(int handle, void* timeData);
    extern "C" void __stdcall CloseDataFile(int handle);
    
    int main()
    {
        std::string path = "K:\\Testfile.036";
        int handle = OpenDataFile(path.c_str());
        if (handle >= 0) {
            // 需定义与Delphi一致的TimeData结构体,循环读取数据
            // TimeData data;
            // while (LoadAndGetNextTimeData(handle, &data) != 0) {
            //     // 处理数据
            // }
            CloseDataFile(handle);
        }
        return 0;
    }
    

    重新编译链接即可解决错误。


解决方法二:动态调用DLL(更可靠,无需生成.lib)

直接通过LoadLibrary和GetProcAddress加载函数,避开静态链接的符号修饰问题:

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

// 定义函数指针类型,严格匹配stdcall调用约定
typedef int (__stdcall* OpenDataFilePtr)(const char*);
typedef int (__stdcall* LoadAndGetNextTimeDataPtr)(int, void*);
typedef void (__stdcall* CloseDataFilePtr)(int);

int main()
{
    // 加载DLL,确保路径正确或DLL在程序运行目录下
    HMODULE dllHandle = LoadLibraryA("MacReadDataFile.dll");
    if (!dllHandle) {
        // 加载失败可通过GetLastError()查看错误码
        return 1;
    }

    // 获取函数地址
    OpenDataFilePtr openFile = reinterpret_cast<OpenDataFilePtr>(GetProcAddress(dllHandle, "OpenDataFile"));
    LoadAndGetNextTimeDataPtr loadNextData = reinterpret_cast<LoadAndGetNextTimeDataPtr>(GetProcAddress(dllHandle, "LoadAndGetNextTimeData"));
    CloseDataFilePtr closeFile = reinterpret_cast<CloseDataFilePtr>(GetProcAddress(dllHandle, "CloseDataFile"));

    if (!openFile || !loadNextData || !closeFile) {
        // 获取地址失败,清理资源
        FreeLibrary(dllHandle);
        return 1;
    }

    // 调用函数打开文件
    std::string filePath = "K:\\Testfile.036";
    int fileHandle = openFile(filePath.c_str());
    if (fileHandle >= 0) {
        // 定义与Delphi一致的TimeData结构体(需根据厂商文档补全)
        // 示例结构体:
        // typedef struct {
        //     double timestamp;
        //     double voltage;
        //     double current;
        //     int cycleNumber;
        // } TimeData;
        // TimeData currentData;

        // 循环读取所有数据
        // while (loadNextData(fileHandle, &currentData) != 0) {
        //     // 处理读取到的数据
        // }

        // 关闭文件
        closeFile(fileHandle);
    }

    // 释放DLL资源
    FreeLibrary(dllHandle);
    return 0;
}

关键注意事项

  • 平台匹配:VS项目的平台(x86/x64)必须与DLL完全一致,否则会出现0xc000007b错误(示例exe无法运行也源于此)。
  • 结构体匹配:TimeData结构体需严格对应Delphi中的定义,包括成员类型、顺序和内存对齐方式,否则读取的数据会错乱。
  • 调用约定:必须使用__stdcall,这是Delphi函数的默认调用约定,不匹配会触发ESP错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 01:50:20