Passing array of struct from c# to delphi
c#, delphi, pinvoke
Solution
Seems like I found the issue:
The code runs fine if .NET 4.0 or higher is used. If you use .NET 3.5 or lower the len-parameter has to be passed by value.
See MSDN-documentation SizeParamIndex v3.5:
The parameter containing the size must be an integer that is passed by value.
See MSDN-documentation SizeParamIndex v4.0:
When arrays are passed as C-style arrays, the marshaler cannot determine the size of the array. Therefore, to pass an managed array to an unmanaged function or method, you must provide two arguments:
The array, defined by reference or value.
The array size, defined by reference or value.
Code working with .NET 3.5:
C#
[DllExport]
public static int func(
[Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)]
Sample[] samples,
int len,
ref int outLen
)
{
// len holds the length of the array on input
// outLen is assigned the number of items that have been assigned values
// use the return value to indicates success and the required array size (>=0) or failure (<0)
int requiredSize = 20;
if (requiredSize < len)
{
len = requiredSize;
}
for (outLen = 0; outLen < len; outLen++)
{
samples[outLen].Name = "foo: " + outLen.ToString();
}
return requiredSize;
}
Delphi7
function func(samples: PSample; len: Integer; var outLen: Integer): Integer; stdcall;
external 'ArrayTest.dll';
procedure Test2;
var
samples: array of TSample;
i, len: Integer;
begin
len := 0;
// query the required array size
i := func(PSample(samples), len, len);
if i>0 then
begin
len := i;
SetLength(samples, len);
if func(PSample(samples), len, len)>=0 then
for i := 0 to len-1 do
Writeln(samples[i].Name);
end;
end;
Conclusion:
The code posted in my question and posted by David Heffernan here only works with .NET >= 4.0! If you have to use .NET <= 3.5 you must pass the arraysize by value and not by reference!
Problem
I am using Robert Giesecke Unmanaged Exports 1.2.6 in VS2010 and my goal is to pass an array of structs from c# (.NET 3.5) to delphi (D7). I have to admit, that I'm not that familiar with delphi. I've already read this post, but the suggested answer didn't work for me: When calling `func` in delphi the CPU-debugging-window opens and if I continue the app exits without exception and without the desired result. Here is the code I tried: C# platform x86 ``` using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.InteropServices; using RGiesecke.DllExport; namespace ArrayTest { public class Class1 { 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; } } } ``` Delphi7 ``` program DelphiApp; {$APPTYPE CONSOLE} uses SysUtils, ActiveX; type TSample = record Name: WideString; end; PSample = ^TSample; function func(samples: PSample; var len: Integer): Integer; stdcall; external 'ArrayTest.dll'; procedure Test2; var samples: array of TSample; i, len: Integer; begin len := 10; SetLength(samples, len); if func(PSample(samples), len)=0 then for i := 0 to len-1 do Writeln(samples[i].Name); end; begin Test2(); end. ``` As mentioned earlier, the debugger opens the CPU-Window and if I continue the app exits without exception or error message. If I run it without dbugger, Windows tells me the app isn't working any more and the app closes. What am I missing? Update Modified code: ``` [DllExport] public static int func( [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] Sample[] samples, ref int len ) { Console.WriteLine("return 0"); return 0; } procedure Test2; var samples: array of TSample; i, len: Integer; begin len := 10; SetLength(samples, len); if func(PSample(samples), len)=0 then for i := 0 to len-1 do Writeln('D7: ', i); end; ``` Even if I don't access the array on either side, the behaviour still is the same. Console-output: `return 0`