Полагаю, вам придется выполнить маршалинг вручную. Объявление функции должно выглядеть так:
[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);
}
}
Если ваша нативная функция будет удерживать и повторно использовать эти области памяти, это не сработает. Если это так, либо задержите освобождение памяти, пока вы не решите, что она больше не используется, либо в нативном методе скопируйте переданные данные и дайте управляемой стороне очистить память сразу после вызова.