How do I pass arrays of struct from .NET to Delphi using unmanaged export (Robert Giesecke)?

StackOverflow https://stackoverflow.com/questions/22507681

  •  17-06-2023
  •  | 
  •  

Question

I have just asked and obtained an answer to my question that was : "can't return custom type instance with unmanaged export (Robert Giesecke)" -> can't return custom type instance with unmanaged export (Robert Giesecke) I wonder if (and how) it is possible to pass arrays of struct from .NET to Delphi using unmanaged export (Robert Giesecke):

  • Returning arrays directly like

[DllExport] public static void CreateSampleInstance(out Sample[] sample)

  • using array member in a returned struct

[DllExport] public static void CreateSampleInstance(out Sample sample)

and

`public struct Sample
{
   Other[] Others;
}`

My question here is how to write the Delphi side and what attribute to set in the .NET one.

Thanks a lot.

Was it helpful?

Solution

Arrays are more tricky because you need to take more care over where the array is allocated and destroyed. The cleanest approach is always to allocate at the caller, pass the array to the callee to let it fill out the array. That approach would look like this in your context:

public struct Sample
{
    [MarshalAs(UnmanagedType.BStr)]
    public string Name;
}

[DllExport]
public static int func(
    [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)]
    Sample[] samples, 
    ref int len
)
{
    // len holds the length of the array on input
    // len is assigned the number of items that have been assigned values 
    // use the return value to indicate success or failure
    for (int i = 0; i < len; i++)
        samples[i].Name = "foo: " + i.ToString();
    return 0;
}

You need to specify that the array needs to be marshalled in the out direction. If you wanted values marshalled both ways then you would use In, Out instead of Out. You also need to use MarshalAs with UnmanagedType.LPArray to indicate how to marshal the array. And you do need to specify the size param so that the marshaller knows how many items to marshal back to the unmanaged code.

And then on the Delphi side you declare the function like this:

type
  TSample = record
    Name: WideString;
  end;
  PSample = ^TSample;

function func(samples: PSample; var len: Integer): Integer; stdcall; 
  external dllname;

Call it like this:

var
  samples: array of TSample;
  i, len: Integer;
....
len := 10;
SetLength(samples, len);
if func(PSample(samples), len)=0 then
  for i := 0 to len-1 do
    Writeln(samples[i].Name);

Update

As AlexS discovered (see comments below), passing the size param index by reference is only supported on .net 4. On earlier versions you need to pass the size param index by value.

The reason I chose to pass it by reference here is to allow for the following protocol:

  1. The caller passes in a value indicating how large the array is.
  2. The callee passes out a value indicating how many elements have been populated.

This works well on .net 4, but on earlier versions you would need to use an extra parameter for step 2.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top