Reputation: 237
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;
}
Upvotes: 1
Views: 805
Reputation: 612954
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];
Upvotes: 5