Question

For our Delphi (XE5) application we are developing an API. In order to communicate data from the Delphi DLL functions with the main program (C based; either (console) C or C++ code applications or Matlab and Simulink) arrays that are allocated by the caller need to be filled with doubles by the DLL.

I understand that open array (Delphi specific) are not very convenient for this purpose as they contain additional data that you then have to mimic in C. Instead I was planning to use pointer arithmetic (see dll function: inc(APDouble)) by directly pointing to the correct address. My question is if this is how you software developers would do this.

A demo is included below (full sources).

The DLL (made in 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.

The C-code caller (made in 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;
}
Was it helpful?

Solution

Your code works fine, but as you observe it is awkward. For interop like this I would bite the bullet and use the $POINTERMATH directive. This allows you to treat a pointer as if it were an array, just as you do in C or C++. For example:

{$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;

Another option would be to copy to a native Delphi array and use that for onward processing. Obviously that involves a copy, but sometimes that's actually what you want. In that case you could do it like this:

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

Or if you don't like the use of Move, a good old loop:

{$POINTERMATH ON}
var
  i: Integer;
  x: TArray<Double>;
....
SetLength(x, len);
for i := 0 to len-1 do
  x[i] := arr[i];
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top