Вы делаете это неправильно.
Вы смешиваете типы указателей.
С помощью команды new TempStruct()
вы создаете массив указателей в TempStruct
. В примере, который я вам дал, был создан массив из TempStruct
. Увидеть разницу?
Теперь ... TempStruct** outPtr
TempStruct*** outPtr
должно быть (потому что вы хотите, чтобы вернуться (*
) массив (*
) указателей (*
) ... Или TempStruct**&
если вы предпочитаете :-)
Изменение этой линии
someData2[i] = (TempStruct)Marshal.PtrToStructure(Marshal.ReadIntPtr(wskptr), typeof(TempStruct));
Потому что вы должны прочитать одиночные указатели.
Я надеюсь, что вы удаляете различные TempStruct
с удалением и использование оператора в
delete[] ptr;
удалить массив структур.
Полный пример:
C++:
struct TempStruct
{
char* str;
int num;
// Note the strdup. You don't know the source of str.
// For example if the source is "Foo", then you can't free it.
// Using strdup solves this problem.
TempStruct(const char *str, int num)
: str(strdup(str)), num(num)
{
}
~TempStruct()
{
free(str);
}
};
extern "C"
{
__declspec(dllexport) void GetResult(TempStruct ***outPtr, int *size)
{
*outPtr = new TempStruct*[2];
(*outPtr)[0] = new TempStruct("sdf", 123);
(*outPtr)[1] = new TempStruct("abc", 456);
*size = 2;
}
__declspec(dllexport) void FreeSomeData(TempStruct **ptr, int size)
{
for (int i = 0; i < size; i++)
{
delete ptr[i];
}
delete[] ptr;
}
}
C#:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1), Serializable]
internal struct TempStruct
{
public string str;
public int num;
}
[DllImport("NativeLibrary.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
static extern void GetResult(out IntPtr outPtr, out int numPtr);
[DllImport("NativeLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
static extern void FreeSomeData(IntPtr ptr, int num);
// C++ will return its TempStruct array in ptr
IntPtr ptr;
int size;
GetResult(out ptr, out size);
TempStruct[] someData2 = new TempStruct[size];
for (int i = 0; i < size; i++)
{
IntPtr ptr2 = Marshal.ReadIntPtr(ptr, i * IntPtr.Size);
someData2[i] = (TempStruct)Marshal.PtrToStructure(ptr2, typeof(TempStruct));
}
// Important! We free the TempStruct allocated by C++. We let the
// C++ do it, because it knows how to do it.
FreeSomeData(ptr, size);
Обратите внимание, что вам не нужно [Serializable]
и Pack=1
на С # struct
Более правильным для C++:
__declspec(dllexport) void GetResult(TempStruct **&outPtr, int &size)
{
outPtr = new TempStruct*[2];
outPtr[0] = new TempStruct("sdf", 123);
outPtr[1] = new TempStruct("abc", 456);
size = 2;
}
Это более правильно, потому как outPtr
и size
не может быть NULL
. См. https://stackoverflow.com/a/620634/613130. Подпись C# такая же.
Вы не означает, например, '(* OutPtr) [0]'? И не должен ли аргумент 'outPtr' быть указателем * tripple * (т. Е.' TempStruct *** outPtr'), поскольку вы выделяете массив указателей? Кастинг, который вы делаете при распределении памяти, маскирует эту последнюю ошибку, никогда не произносите результат 'new'. –