Не могу маршалировать массив структур из C ++ в C # в Unity

Я пытаюсь передать массивstructс C ++ на сценарий Unity на C #. Когда я использую код в рабочей среде, размер массива будет сильно различаться, поэтому мне фактически нужно передать массив неизвестной длины.

Моя умная идея состояла в том, чтобы сохранить массив в куче и передать ссылку на этот массив в Unity. Я нашел сообщения StackOverflow о том, как это сделать. Но тогда Unity жалуется, что ссылка пуста.

Вот часть моего кода на C ++:

extern "C" struct S; // Not currently used.

struct S {
  int i;
  float f;
};

extern "C" bool helloWorld(S ** a, int * i);

S * s;

bool helloWorld(S ** a, int * i) {
  s = new S[4];
  for (int j = 0; j < 4; j++) {
    s[j].i = j;           // Fill in placeholder
    s[j].f = (float) j;   // data for now.
  }
  *a = s; // I don't know if this works.
  *i = 4; // Works.
  return true;
}

Я пробовал это сintс вместоstructи это сработало.

Теперь мой код C #:

[StructLayout(LayoutKind.Sequential),Serializable]
public struct S {
  int i;
  float f;
};

void Start() {
  IntPtr ptrNativeData = IntPtr.Zero;
  int itemsLength = 0;

  bool success = helloWorld(ref ptrNativeData, ref itemsLength);
  if (!success) {
    return;
  }

  S[] SArray = new S[itemsLength];  // Where the final data will be stored.
  IntPtr[] SPointers = new IntPtr[itemsLength];

  Debug.Log("Length: " + itemsLength); // Works!
  Marshal.Copy(ptrNativeData, SPointers, 0, itemsLength); // Seems not to work.

  for (int i = 0; i < itemsLength; i++) {
    Debug.Log("Pointer: " + SPointers[i]); // SPointers[0] prints 0.
    Marshal.PtrToStructure(SPointers[i], SArray[i]); // Crashes here. Boom.
  }
}

[DllImport("TestUnity")]
private static extern bool helloWorld(ref IntPtr ptrResultVerts,
    ref int resultVertLength);

Marshal.PtrToStructure Инструкция говорит, что аргумент SPointers [i] является нулевым. Я проверил с помощью команды Debug.Log, и на самом деле он кажется нулевым: он печатает как 0.

Но я попробовал нечто подобное с массивомintраньше, и это сработало. В чем я не уверен: моя проблема в C ++ или в C #? Я не передаю правильную информацию или неправильно обрабатываю?

Решение 1

Это было первое решение, которое я придумал в основном самостоятельно. Второе решение лучше.

Абсолютно благодаря Алексу Скалозубу, я понял это. Во время маршаллинга абстрагируется один уровень косвенности указателя. Итак, теперь мой код C ++ содержит:

S ** s; // Not a pointer to Ss, but a pointer to pointers to Ss.

bool helloWorld(S *** a, int * i) { // Pass a triple pointer.
  s = new S * [4]; // Create array of pointers.
  for (int j = 0; j < 4; j++) {
    s[j] = new S; // Actually create each object.
    s[j]->i = j;
    s[j]->f = (float) j;
  }
  *a = s;
  *i = 4;
  return true;
}

И мой код C # содержит:

for (int i = 0; i < itemsLength; i++) {
  Debug.Log("Pointer: " + SPointers[i]);
  SArray[i] = (S) Marshal.PtrToStructure(SPointers[i], typeof(S));
}

И это все! Все данные передаются.

Теперь это оставляет меня с проблемой управления памятью:каждый отдельный объект, созданный в коде C ++, должен быть освобожден, Я знаю об этом, и я позабочусь об этом дальше.

Решение 2

Смотрите проверенный ответ. Ответ Алекса намного лучше, поскольку он намного меньше использует ненужные указатели. Я использовал больше указателей, потому что я только что понял, как использовать их в C #.

Ответы на вопрос(2)

Ваш ответ на вопрос