Как я маршалирую указатель на массив указателей на структуры?

У меня есть функция C со следующей сигнатурой:

int my_function(int n, struct player **players)

players - это указатель на массив указателей на объекты struct player. n - количество указателей в массиве. Функция не изменяет массив и содержимое структур, и после возвращения не сохраняет указателей.

Я попробовал следующее:

[DllImport("mylibary.dll")]
static extern int my_function(int n, 
    [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] 
     player_in []players);

Однако это упорядочивает данные как указатель на массив структур, а не на указатель на массив указателей на структуры.

Ответ 1

Я считаю, что вам придется делать некоторые из маршалингов вручную. Объявление функции должно выглядеть следующим образом:

[DllImport("mylibary.dll")]
private static extern int my_function(int n, IntPtr players);

Нам нужно выделить некоторую внутреннюю память и перенести ее структуры, прежде чем передавать ее в нативную функцию:

private static void CallFunction(Player[] players)
{
    var allocatedMemory = new List<IntPtr>();

    int intPtrSize = Marshal.SizeOf(typeof(IntPtr));
    IntPtr nativeArray = Marshal.AllocHGlobal(intPtrSize * players.Length);
    for (int i = 0; i < players.Length; i++)
    {
        IntPtr nativePlayer = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Player)));
        allocatedMemory.Add(nativePlayer);
        Marshal.StructureToPtr(players[i], nativePlayer, false);

        Marshal.WriteIntPtr(nativeArray, i * intPtrSize, nativePlayer);
    }

    my_function(players.Length, nativeArray);

    Marshal.FreeHGlobal(nativeArray);

    foreach (IntPtr ptr in allocatedMemory)
    {
        Marshal.FreeHGlobal(ptr);
    }
}

Если ваша собственная функция будет удерживаться и повторно использовать эти ячейки памяти, это не сработает. Если это так, либо удерживайте его, освобождая память, пока не подумайте, что она больше не используется, или в нативном методе скопируйте переданные данные и позвольте управляемой стороне очистить память сразу после вызова.