2015-06-24 5 views
2

Ниже у меня есть фрагмент кода из C++. Мне нужно вернуть массив указателей (в TempStruct).Возвращает массив указателей от C++ до C#

Проблема в том, что на стороне C# я получаю только один элемент. С другой стороны, я получаю AV.

**C++** 
extern "C" __declspec(dllexport) void GetResult(TempStruct** outPtr, long *size) 
{  
    *outPtr = (TempStruct*)new TempStruct*[2]; 

    outPtr[0] = new TempStruct("sdf", 123); 
    outPtr[1] = new TempStruct("abc", 456); 

    *size = 2;  
} 

**C#**  
[DllImport("test.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] 
public static extern void GetResult(out IntPtr outPtr, out int size); 

IntPtr ptr = IntPtr.Zero; 
int length;   
GetResult(out ptr, out length); 

int size = Marshal.SizeOf(typeof(TempStruct)); 
TempStruct[] someData2 = new TempStruct[length]; 

for (int i = 0; i < length; i++) 
{ 
    IntPtr wskptr = (IntPtr)(ptr.ToInt64() + (size * i)); 
    someData2[i] = (TempStruct)Marshal.PtrToStructure(wskptr, typeof(TempStruct));    
} 
+1

Вы не означает, например, '(* OutPtr) [0]'? И не должен ли аргумент 'outPtr' быть указателем * tripple * (т. Е.' TempStruct *** outPtr'), поскольку вы выделяете массив указателей? Кастинг, который вы делаете при распределении памяти, маскирует эту последнюю ошибку, никогда не произносите результат 'new'. –

ответ

1

Вы делаете это неправильно.

Вы смешиваете типы указателей.

С помощью команды new TempStruct() вы создаете массив указателей в TempStruct. В примере, который я вам дал, был создан массив из TempStruct. Увидеть разницу?

Теперь ... TempStruct** outPtrTempStruct*** 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# такая же.

+0

Еще я могу читать только 1-й предмет. Я изменил параметр на тройные указатели и изменил код C#, как вы писали выше – John

+0

'delete [] ptr' недостаточно.Код в вопросе требует, чтобы каждый элемент также был удален. Вот почему я считаю, что код на C++ неверен. –

+0

@DavidHeffernan Yep ... Исправлено. – xanatos

3

Код C++ неверен. Он возвращает массив указателей на struct. Тот факт, что вы указали значение, возвращаемое new, должно было предупредить вас о том, что вы допустили ошибку. Вы хотите вернуть массив структуры.

Оно должно быть:

*outPtr = new TempStruct[2]; 
(*outPtr)[0].str = "sdf"; 
(*outPtr)[0].i = 123; 
(*outPtr)[1].str = "abc"; 
(*outPtr)[1].i = 456; 
*size = 2;  
+0

Не * ты * означает, например, '(* OutPtr) [0]'? :) –

+0

@JoachimPileborg Да. Спасибо. Я просто мусор на C++! –

+0

@DavidHeffernan Единственная проблема заключается в том, что вы не можете легко использовать параметризованные конструкторы с этим синтаксисом. Кто-то спросил, что [здесь] (http://stackoverflow.com/q/4754763/613130) и решения, где это ужасно. Можно использовать [этот] (http://stackoverflow.com/a/4756306/613130), но я бы предпочел пойти к дантисту ... :-) – xanatos

Смежные вопросы