Home > Enterprise >  Using Arrays in DLL written in Delphi
Using Arrays in DLL written in Delphi

Time:09-17

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))
  • Related