从 C++ DLL 编辑 Delphi 记录

Posted

技术标签:

【中文标题】从 C++ DLL 编辑 Delphi 记录【英文标题】:Edit Delphi record from C++ DLL 【发布时间】:2017-12-06 02:25:12 【问题描述】:

我正在尝试使用从 Delphi 5 程序调用的 Visual C++ 创建一个 DLL。 Delphi 程序传入一条记录,然后在 DLL 中对其进行编辑,Delphi 程序使用该结果。

例如,Delphi 代码类似如下:

Type dll_btvar = record
    age : smallint;
    name : array[0..11] of char;
    value : Double;
end;

// Import the function from the dll
function foo(CVars : dll_btvar):integer; external 'example.dll';

// Call the dll
function callFoo(var passedVar:dll_btvar):integer;
begin
    result := foo(passedVar);
    // Use passedVar.value
end;

C++ 代码示例:

在example.h中:

#pragma once
#include "dllVar.h"
extern "C" 
    __declspec(dllexport) int foo(DLL_Var var);

在example.cpp中:

#include "example.h"
int foo(DLL_Var var)
    var.value = var.age + var.name[0];
    return 0;

在 dllVar.h 中:

#pragma once
#pragma pack(8)
extern "C" 
    struct DLL_Var 
        short age;
        char name[12];
        double value;
    

我使用#pragma pack(8),因为该值提供了正确的对齐方式,以便在 DLL 中正确读取传递的记录。

在示例代码中,当传递年龄和姓名时,我希望由 DLL 设置值,然后可以在 Delphi 程序中恢复。结果将是某种错误代码。

在 C++ Builder 5 中使用相同的代码确实有效,但是它当然已经过时了,我还没有移动我的 DLL 中的所有代码(我也不想),只有你在这里看到的最低限度。

我测试了几种让 Delphi 将地址/指针传递给 dll 的方法,但是它们并没有改变任何东西。

现在,返回值发送正确,但记录的字段(即值)保持不变。

我需要对 Delphi 或 C++ 进行哪些更改才能捕获传递记录中的更改?我很高兴广泛使用 C++,但我更愿意将 Delphi 更改保持在最低限度,因为这是我不想破坏的旧软件。

【问题讨论】:

【参考方案1】:
function foo(CVars : dll_btvar):integer; external 'example.dll';

问题从这里开始,在 Delphi 代码中。记录是按值传递的。也就是说,调用者的记录变量被复制到一个新变量,然后传递给函数。这意味着调用者看不到被调用者对此记录副本的修改。因此,您必须将参数作为var 参数传递:

function foo(var CVars : dll_btvar):integer; external 'example.dll';

下一个问题是调用约定。您必须对双方使用相同的调用约定。您的代码在 Delphi 端使用默认的 register 约定,非 Borland/Embarcadero 工具不支持该约定。请改用stdcallcdecl。让我们选择cdecl,这是大多数 C++ 工具的默认设置:

function foo(var CVars : dll_btvar):integer; cdecl; external 'example.dll';

要使 C++ 代码匹配,请通过引用传递参数:

__declspec(dllexport) int __cdecl foo(DLL_Var &var);

或者显式使用指针:

__declspec(dllexport) int __cdecl foo(DLL_Var *var);

在后一个选项中,由于使用了指针,需要更改实现:

int foo(DLL_Var *var)
    var->value = var->age + var->name[0];
    return 0;

在 C++ Builder 5 中使用相同的代码确实有效。

没有,因为您问题中的 Delphi 代码无法修改调用者的记录。

【讨论】:

另外请注意,如果 Delphi 代码升级到 Delphi 2009 或更高版本,Delphi 的 Char 类型将从 AnsiChar 更改为 WideChar,因此 Delphi 记录必须从 @ 更改987654335@ 到 AnsiChar 以避免破坏 DLL。现在可能值得进行更改以避免以后必须进行更改。 谢谢!问题似乎是不使用var,以及我不需要明确说cdecl这一天真的假设。我刚刚重新检查了我所说的关于它在 C++ Builder 5 中工作的内容,我可以确认它确实改变了 Delphi 程序使用的记录 @Johnny678 不,它没有。调用者看不到对按值传递的参数的修改。毫无疑问,您正在查看的代码与您发布的代码不同。 @DavidHeffernan 感谢您的耐心等待。如果我要为这个问题添加更多细节,您能否/愿意解释为什么我期望通过值传递的结果不是会发生什么?

以上是关于从 C++ DLL 编辑 Delphi 记录的主要内容,如果未能解决你的问题,请参考以下文章

编写从 C++ 应用程序链接的 Delphi DLL:访问 C++ 接口成员函数会导致访问冲突

AnyDAC - 编辑前刷新记录

如何从 Delphi 代码动态编辑 pdf 中的字段?

关于delphi调用C++的DLL中char*参数的问题

Delphi调用C++编写的DLL

要从 Delphi 调用的 C++ dll 函数 - 数组参数