I have some functions written in Delphi that I need to access from other programming languages (e.g. Python) via a DLL. The Delphi-functions take either some kind of numbers (integers or doubles), arrays or Matrizes (array of array) as input and return type.
Passing integers works very well with a simple test function, in Delphi:
library Delphi_Library;
uses
System.SysUtils;
function AddIntegers(const _a, _b: integer): integer; stdcall;
begin
Result := _a _b;
end;
exports
AddIntegers;
begin
end.
and calling it from Python:
import ctypes
Dll = ctypes.WinDLL('Delphi_Library')
a = ctypes.c_int(5)
b = ctypes.c_int(7)
c = Dll.AddIntegers(a, b)
print(c)
However, I cannot directly pass an array to the DLL, but as I understood I need to use a pointer to the array. Additionaly I cannot use an array as return type, so instead I want to alter an input array. As test function I want to write a small function, that takes a pointer to an array array_in
and it's length size_array_in
as input and sets another array array_out
and it's size size_array_out
to the same values. It also returns the last element of the array as double.
My code in Delphi looks like:
library Delphi_Library;
uses
System.SysUtils;
type
TArray = Array of Double;
PArray = ^TArray;
function ReturnArray(array_in: pointer; size_array_in: integer; array_out: pointer; size_array_out: integer): Double; stdcall;
var
i: Integer;
P_value: PDouble;
arr: TArray;
begin
Result := 0;
P_value := PDouble(array_in);
setlength(arr, size_array_out);
//arr := P_in^;
for i := 0 to size_array_in-1 do
begin
arr[i] := P_value^;
inc(P_value);
end;
array_out := @arr;
size_array_out := Length(arr);
end;
exports
ReturnArray;
begin
end.
and in Python
ReturnArray = Dll.ReturnArray
ReturnArray.restype = ctypes.c_double
ReturnArray.argtypes = ctypes.POINTER(ctypes.c_double*8), ctypes.c_int, ctypes.POINTER(ctypes.c_double*8), ctypes.c_int
data_in = (ctypes.c_double*8)(*range(32, 40))
data_out = (ctypes.c_double*8)(*range(8))
num_1 = 8
num_2 = 8
print('input', list(data_in), list(data_out))
retval = ReturnArray(data_in, num_1, data_out, num_2)
print('output', retval, list(data_in), list(data_out))
However I still get the same array/list for data_out
after calling the Delphi-DLL-function.
So how do I parse an array or a pointer to an array from another programming language to a Delphi-Array? My ultimate goal is to write an interface to be able to use a Delphi function with a definition like function AnyFunction(a: Array of Double; b: Array of Array of Double): Array of Double
from any programming language that supports calling C-DLLs.
CodePudding user response:
Delphi's dynamic arrays are simply not compatible with other programming languages/compilers (except C Builder). So you need to operate on raw pointers only. In this case, the caller needs to allocate both arrays and pass in pointers to them, then the Delphi code can simply copy values from one array to the other, eg:
library Delphi_Library;
uses
System.SysUtils, System.Math;
{$POINTERMATH ON}
function ReturnArray(array_in: PDouble; size_array_in: Integer;
array_out: PDouble; size_array_out: Integer): Integer; stdcall;
var
i, len: Integer;
begin
len := Min(size_array_in, size_array_out);
for i := 0 to len-1 do
begin
array_out[i] := array_in[i];
// or, if {$POINTERMATH} is not available in your Delphi version:
// array_out^ := array_in^;
// Inc(array_in);
// Inc(array_out);
end;
// Or simpler:
// Move(array_in^, array_out^, len * sizeof(Double));
Result := len;
end;
exports
ReturnArray;
begin
end.
ReturnArray = Dll.ReturnArray
ReturnArray.restype = ctypes.c_int
ReturnArray.argtypes = ctypes.POINTER(ctypes.c_double*8), ctypes.c_int, ctypes.POINTER(ctypes.c_double*8), ctypes.c_int
data_in = (ctypes.c_double*8)(*range(32, 40))
data_out = (ctypes.c_double*8)(*range(8))
num_1 = 8
num_2 = 8
print('input', list(data_in), list(data_out))
retval = ReturnArray(data_in, num_1, data_out, num_2)
print('output', retval, list(data_in), list(data_out))
My ultimate goal is to write an interface to be able to use a Delphi function with a definition like
function AnyFunction(a: Array of Double; b: Array of Array of Double): Array of Double
from any programming language that supports calling C-DLLs.
Delphi open array parameters are nothing more than compiler magic to hide a pair of parameters that consist of:
- a raw pointer to the array's 1st element.
- the last index (not the length!) of the array.
So, for example, the above example could be re-written using open array parameters like this:
library Delphi_Library;
uses
System.SysUtils, System.Math;
function ReturnArray(array_in: array of Double;
array_out: array of Double): Integer; stdcall;
var
i, len: Integer;
begin
len := Min(Length(array_in), Length(array_out));
for i := 0 to len-1 do
begin
array_out[i] := array_in[i];
end;
// Or simpler:
// Move(array_in[0], array_out[0], len * sizeof(Double));
Result := len;
end;
exports
ReturnArray;
begin
end.
ReturnArray = Dll.ReturnArray
ReturnArray.restype = ctypes.c_int
ReturnArray.argtypes = ctypes.POINTER(ctypes.c_double*8), ctypes.c_int, ctypes.POINTER(ctypes.c_double*8), ctypes.c_int
data_in = (ctypes.c_double*8)(*range(32, 40))
data_out = (ctypes.c_double*8)(*range(8))
num_1 = 7 # NOT 8!
num_2 = 7 # NOT 8!
print('input', list(data_in), list(data_out))
retval = ReturnArray(data_in, num_1, data_out, num_2)
print('output', retval, list(data_in), list(data_out))