Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
179 views
in Technique[技术] by (71.8m points)

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

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.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...