【问题标题】:Pass double array by reference from C to Delphi DLL通过引用从 C 传递双数组到 Delphi DLL
【发布时间】:2014-06-11 06:24:58
【问题描述】:

对于我们的 Delphi (XE5) 应用程序,我们正在开发一个 API。为了将来自 Delphi DLL 函数的数据与主程序(基于 C;(控制台)C 或 C++ 代码应用程序或 Matlab 和 Simulink)进行通信,调用者分配的数组需要由 DLL 填充双精度值。

我知道开放数组(Delphi 特有的)对于这个目的不是很方便,因为它们包含额外的数据,然后你必须在 C 中模仿这些数据。相反,我打算使用指针算法(参见 dll 函数:inc(APDouble))通过直接指向正确的地址。我的问题是,如果您的软件开发人员会这样做。

下面包含一个演示(完整来源)。

DLL(在 DXE5 中制作):

library PDA;

uses
  System.StrUtils,
  System.SysUtils,
  Vcl.Dialogs;

{$R *.res}

function ShowArrayContents( APDouble: PDouble;
                            size: Integer): integer; cdecl; export;
var
  i: Integer;
begin
  Result := 0;
  for i := 0 to size-1 do
  begin
    // Show value!
    MessageDlg(Format('%p -> %p -> %f', [@APDouble, APDouble, APDouble^]), mtWarning, [mbOK], 0);
    Inc(APDouble);
  end;
end;

exports
  ShowArrayContents;

begin

end.

C 代码调用者(在 C++ builder XE4 中制作):

#include <stdio.h>
#include <tchar.h>
#include <stdlib.h>
#include <windows.h>

typedef int (*_ShowArrayContents) (double *, int);

char *dllname = "PDA.dll";
static HINSTANCE hInstanceControl;

_ShowArrayContents ShowArrayContents = NULL;

int _tmain(int argc, _TCHAR* argv[])
{
    double DVals[3] = {1.23, 4.56, 7.89};
    int i;

    hInstanceControl = LoadLibrary(dllname);

    if( hInstanceControl != NULL){
        ShowArrayContents =(_ShowArrayContents)GetProcAddress(hInstanceControl, "ShowArrayContents");
    } else {
        return 0;
    }

    // test program:
    (*ShowArrayContents)(&DVals[0], 3);

    FreeLibrary(hInstanceControl);

    system("pause");
    return 0;
}

【问题讨论】:

    标签: c arrays delphi dll


    【解决方案1】:

    您的代码运行良好,但您观察到它很尴尬。对于这样的互操作,我会硬着头皮使用$POINTERMATH 指令。这允许您将指针视为数组,就像在 C 或 C++ 中一样。例如:

    {$POINTERMATH ON}
    function GetSum(arr: PDouble; len: Integer): Double; cdecl; 
    var
      i: Integer;
    begin
      Result := 0.0;
      for i := 0 to len-1 do
        Result := Result + arr[i];
    end;
    

    另一种选择是复制到本机 Delphi 数组并将其用于后续处理。显然,这涉及到副本,但有时这正是您想要的。在这种情况下,您可以这样做:

    var
      x: TArray<Double>;
    ....
    SetLength(x, len);
    Move(arr^, Pointer(x)^, len*SizeOf(arr^));
    

    或者如果您不喜欢使用Move,可以使用一个很好的旧循环:

    {$POINTERMATH ON}
    var
      i: Integer;
      x: TArray<Double>;
    ....
    SetLength(x, len);
    for i := 0 to len-1 do
      x[i] := arr[i];
    

    【讨论】:

    • 感谢您对使用 POINTERMATH 的评论!我认为让调用者处理内存分配是最佳实践。那么使用本地副本有什么好处呢?
    • 如果您需要将数组传递给一些适用于数组的后续方法,您可能需要进行复制。如果您想保留数组并用于将来的调用,那么您会这样做。不过你说的很对,调用者分配是最佳实践。
    • 非常感谢您的 cmets!我想我坚持使用对“数组”的引用。我只希望 DLL 放置一些值,以便调用者可以使用它们。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-20
    • 1970-01-01
    • 2013-02-26
    • 1970-01-01
    相关资源
    最近更新 更多