Я только что спросил и получил ответ на мой вопрос: "не удается вернуть экземпляр экземпляра с неуправляемым экспортом (Robert Giesecke)" → не может вернуть пользовательские типа с неуправляемым экспортом (Robert Giesecke) Интересно, можно ли (и как) передать массивы структуры из .NET в Delphi с использованием неуправляемого экспорта (Robert Giesecke):
[DllExport] public static void CreateSampleInstance(out Sample[] sample)
[DllExport] public static void CreateSampleInstance(out Sample sample)
и
`public struct Sample
{
Other[] Others;
}`
Мой вопрос здесь в том, как написать сторону Delphi и какой атрибут установить в .NET.
Большое спасибо.
Массивы более сложны, потому что вам нужно больше заботиться о том, где массив выделяется и уничтожается. Самый чистый подход - всегда выделять у вызывающего, передать массив вызываемому, чтобы он заполнил массив. Такой подход будет выглядеть в вашем контексте:
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;
}
Вам нужно указать, что массив должен быть настроен в направлении out. Если вы хотите, чтобы значения были распределены в обоих направлениях, вы использовали бы In, Out
вместо Out
. Вам также нужно использовать MarshalAs
с UnmanagedType.LPArray
, чтобы указать, как маршалировать массив. И вам нужно указать параметр размера, чтобы маршаллер знал, сколько элементов отправляется обратно к неуправляемому коду.
И затем на стороне Delphi вы объявите функцию следующим образом:
type
TSample = record
Name: WideString;
end;
PSample = ^TSample;
function func(samples: PSample; var len: Integer): Integer; stdcall;
external dllname;
Назовите его следующим образом:
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);
Обновление
Как обнаружено AlexS (см. комментарии ниже), передача индекса параметра размера по ссылке поддерживается только на .net 4. В более ранних версиях вам необходимо передать размер параметр param по значению.
Причина, по которой я решил передать его по ссылке, - это разрешить следующий протокол:
Это хорошо работает на .net 4, но в более ранних версиях вам нужно будет использовать дополнительный параметр для шага 2.
function (var samples: PSample; var len:Integer) : Integer; stdcall;
перед сэмплами вот так:function (var samples: PSample; var len:Integer) : Integer; stdcall;