Как передать массивы struct из .NET в Delphi с помощью неуправляемого экспорта (Роберт Гизеке)?

2

Я только что спросил и получил ответ на мой вопрос: "не удается вернуть экземпляр экземпляра с неуправляемым экспортом (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.

Большое спасибо.

Теги:
pinvoke

1 ответ

3
Лучший ответ

Массивы более сложны, потому что вам нужно больше заботиться о том, где массив выделяется и уничтожается. Самый чистый подход - всегда выделять у вызывающего, передать массив вызываемому, чтобы он заполнил массив. Такой подход будет выглядеть в вашем контексте:

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.

  • 0
    Спасибо ! Я должен был поместить function (var samples: PSample; var len:Integer) : Integer; stdcall; перед сэмплами вот так: function (var samples: PSample; var len:Integer) : Integer; stdcall;
  • 0
    извините, но если я строго буду использовать вашу рекомендацию, я получу пустой массив только после вызова моей функции .NET. Если я добавлю ключевое слово ref в параметр массива моей функции .NET, это будет работать! Кстати, я создаю массив на стороне delphi, передаю его .NET, который заполняет его, и возвращаю обратно в delphi.
Показать ещё 16 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню