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

如何在Delphi中调用含特定函数的C++ DLL?类型匹配问题求助

问题描述

我有一个包含特定C函数的DLL,在C环境中调用CreateLoggerWithLogs完全正常,但用Delphi按自己的方式声明并调用时,一定会触发“External exception”错误。我有三个疑问:

  1. 如何处理Delphi与C++的类型转换?
  2. 调用CreateLogger创建的类静态实例是否和C++中一样,在调用析构函数前一直存活?
  3. 如何用Delphi代码调试这个C++ DLL?

C++函数定义

#ifdef LOGGER_EXPORTS
#define LOGGER_API __declspec(dllexport)
#else
#define LOGGER_API __declspec(dllimport)
#endif

extern "C" LOGGER_API void LogMessage(const json& message);

extern "C" LOGGER_API std::vector<json> getCommands();

extern "C" LOGGER_API void CreateLogger(const std::string& infilename, const std::string& outfilename, const std::string& idinfilename, const std::string& idoutfilename);

extern "C" LOGGER_API void CreateLoggerWithLogs(const std::string& infilename, const std::string& outfilename, const std::string& idinfilename, const std::string& idoutfilename,
    const std::string& logInput, const std::string& logOutput);

Delphi函数声明

procedure LogMessage(const message: TJSONObject); cdecl;
  external 'ConnectionDLL.dll';

function getCommands(): TJSONObject; cdecl; external 'ConnectionDLL.dll';

procedure CreateLogger(const infilename, outfilename, idinfilename,
  idoutfilename: PAnsiChar); cdecl; external 'ConnectionDLL.dll';

procedure CreateLoggerWithLogs(const infilename, outfilename, idinfilename,
  idoutfilename, logInput, logOutput: PAnsiChar); cdecl;
  external 'ConnectionDLL.dll';

Delphi调用代码

var
  infilename, outfilename, idinfilename, idoutfilename, logInput, logOutput: PAnsiChar;
begin
    infilename := 'input_filename';
    outfilename := 'output_filename';
    idinfilename := 'id_input_filename';
    idoutfilename := 'id_output_filename';
    logInput := 'log_input_filename';
    logOutput := 'log_output_filename';

    CreateLoggerWithLogs(infilename, outfilename, idinfilename, idoutfilename,
      logInput, logOutput);
end;
解决方案

1. Delphi与C++的类型转换问题

你的Delphi函数声明存在多处关键类型不匹配,这是触发异常的核心原因,修正方式如下:

  • std::string& 与 PAnsiChar 不匹配:C的std::string&是引用类型,Delphi传递的PAnsiChar是原始指针,C内部对std::string的内存操作会因无对应对象管理导致访问错误。直接用Delphi的AnsiString(加const修饰)即可兼容,因为cdecl约定下两者底层结构匹配。
    修正后的Delphi声明:
    procedure CreateLogger(const infilename, outfilename, idinfilename, idoutfilename: AnsiString); cdecl; external 'ConnectionDLL.dll';
    procedure CreateLoggerWithLogs(const infilename, outfilename, idinfilename, idoutfilename, logInput, logOutput: AnsiString); cdecl; external 'ConnectionDLL.dll';
    
    调用时直接用AnsiString变量,无需转PAnsiChar:
    var
      infilename, outfilename, idinfilename, idoutfilename, logInput, logOutput: AnsiString;
    begin
        infilename := 'input_filename';
        outfilename := 'output_filename';
        idinfilename := 'id_input_filename';
        idoutfilename := 'id_output_filename';
        logInput := 'log_input_filename';
        logOutput := 'log_output_filename';
    
        CreateLoggerWithLogs(infilename, outfilename, idinfilename, idoutfilename, logInput, logOutput);
    end;
    
  • json& 与 TJSONObject 不匹配:C的json(如nlohmann/json)和Delphi的TJSONObject内存结构完全不同,直接传递会导致DLL解析崩溃。改成传递JSON字符串:
    修改C
    函数:
    extern "C" LOGGER_API void LogMessage(const std::string& message_str);
    // 内部实现:json message = json::parse(message_str);
    
    Delphi声明:
    procedure LogMessage(const message: AnsiString); cdecl; external 'ConnectionDLL.dll';
    
  • std::vector<json> 与 TJSONObject 不匹配:C容器类型无法直接映射到Delphi对象,改成返回JSON数组字符串:
    修改C
    函数:
    extern "C" LOGGER_API std::string getCommands();
    // 内部实现:return json(getCommands()).dump();
    
    Delphi声明:
    function getCommands(): AnsiString; cdecl; external 'ConnectionDLL.dll';
    
    Delphi拿到字符串后再解析成TJSONArray或TJSONObject即可。

2. 静态实例的存活周期

调用CreateLogger创建的C静态类实例,存活逻辑和C环境中完全一致:只要DLL未被卸载,且未主动调用析构函数,实例会一直存活。

注意:如果Delphi程序调用FreeLibrary卸载DLL,静态实例会被销毁;若不卸载DLL,实例会存活到进程退出。

3. Delphi调试C++ DLL的方法

  • Visual Studio附加进程调试:打开C++ DLL项目,点击「调试」→「附加到进程」,选择运行中的Delphi程序进程,在C代码中设置断点,Delphi调用DLL函数时会触发断点,进入C调试。
  • 确保生成调试符号:C++ DLL编译时要生成.pdb调试符号文件,并和DLL放在同一目录,调试时才能看到完整的代码和变量信息。
  • Delphi输出调试日志:在调用DLL前后用OutputDebugString输出日志,配合DebugView工具查看,帮助定位调用时机和参数是否正确。
  • 内存错误检测:若异常是内存访问错误,用Visual Studio的AddressSanitizer编译DLL,运行Delphi程序时会捕获非法内存访问的具体位置。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 14:35:38