3

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.

1 Answer 1

3

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.

Sign up to request clarification or add additional context in comments.

16 Comments

Thank you ! I had to put a var before samples like that : function (var samples: PSample; var len:Integer) : Integer; stdcall;
sorry, but if I strictly use your recommandation I only get an empty array after calling my .NET function. If I put a ref keyword on the array parameter of my .NET function, it works ! btw I create the array on delphi side, give it to .NET which fill it and return it back to delphi.
You should remove that Array.Resize. Let me just double check and test the code.
so I have removed the Array.Resize(ref samples, len); line but it stills raise an exception. The only way I managed to make it works is by having a var samples parameter in the delphi function, a ref samples parameter in the .NET function and a Array.Resize() in the .NET function.
No, there is a problem. I will work it out and update my answer.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.