2

I want to use delphi array function in C#.

My delphi code:

TIntegerArray = array of integer;
function Testarray(): TIntegerArray stdcall; export;
 var
   res: TIntegerArray2;
 begin
   SetLength(res, 10);
   res[5] := 55;
   Result := res;
 end;
 exports Testarray;

C# code:

[DllImport("GitaTele.dll", CallingConvention = CallingConvention.StdCall)]
public static extern int[] Testarray();

Shows me error:

Cannot marshal 'return value': Invalid managed/unmanaged type combination.

Milo
  • 3,365
  • 9
  • 30
  • 44
Ali Yousefi
  • 2,355
  • 2
  • 32
  • 47
  • 1
    In Delphi you return a specific type `TIntegerArray2` while in C# you try to map this to `int[]`. Howe is this supposed to work? – Quality Catalyst May 12 '15 at 05:40
  • You can't marshal directly an array **as the retun value**. And even if you could, who would free the memory Delphy allocated for that array? – xanatos May 12 '15 at 05:41
  • thank you. edited.my code is more than: TIntegerArray = array of integer; TIntegerArray2 = array of integer; – Ali Yousefi May 12 '15 at 05:42
  • You cannot use arrays as a function result across DLL boundaries. Simple as that. Same reason why you cannot return strings as function results. – Jerry Dodge May 12 '15 at 05:43
  • Pass it as a var parameter instead. But it's not as simple as that. You also have to decide which side is responsible for memory allocation / freeing. The answer varies depending on that. – Jerry Dodge May 12 '15 at 05:49

1 Answers1

3

Delphi dynamic arrays are not valid interop types. You need to have the caller allocate the array, and then let the callee populate it.

procedure PopulateArray(arr: PInteger; var len: Integer); stdcall;
var
  i: Integer;
  returnArray: TArray<Integer>;
begin
  returnArray := GetArray;
  len := Min(len, Length(returnArray));
  for i := 0 to len - 1 do
  begin
    arr^ := returnArray[i];
    inc(arr);
  end;
end;

Do note the export has no meaning, is ignored, and should be removed for sake of simplicity.

From C# the calling code is:

[DllImport("...", CallingConvention = CallingConvention.StdCall)]
public static extern void PopulateArray(
    [In, Out]
    [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)]
    int[] arr,
    ref int len
);

....

int[] arr = new int[50];
int len = arr.Length;
PopulateArray(arr, ref len);
// len now contains actual length
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Why does it needs the whole `MarshalAs`? Can't he marshal the array directly to a `PInteger`, that is a `int*`? – xanatos May 12 '15 at 12:42
  • You can do without the `MarshalAs`, but it allows the marshaller to optimise marshalling. On the other hand, since this example is blittable then the marshaller will just pin the array. So in that sense, `MarshalAs` serves no purpose. – David Heffernan May 12 '15 at 12:47
  • @DavidHeffernan `MarshalAs(MarshalAs(` you doubled the `MarshalAs` in your code. – xanatos May 12 '15 at 13:01
  • @xanatos Belt **and** braces ;-) – David Heffernan May 12 '15 at 13:02