Передача массива строк из c # в c ++ - управление памятью - PullRequest
1 голос
/ 13 июля 2011

В моем коде у меня есть c DLL, которая принимает массив строк: void Helper :: ProcessEvent (PEVENT_RECORD pEvent, wchar_t ** OutPutFormattedData)

Я вызываю ее с этим кодом:

[DllImport("Helper.dll", EntryPoint = "ProcessEvent")]
    internal static extern uint ProcessEvent(
        [In, Out] 
        ref EVENT_RECORD pEvent,
        [In, Out] 
        [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStr)] ref string[] pResult);

В коде c ++ вот основной код, который я использую для заполнения массива:

for(int i=0;i<list.Size;i++)
{
    EventWrapperClass *EWC = new EventWrapperClass();
    EWC = list.Events[i];
    OutPutFormattedData[i] = new wchar_t [wcslen(HelperFormatMessage(L"%s: %s\n",EWC->GetProperyName(),EWC->GetProperyValue()))+1];
    wcscpy(OutPutFormattedData[i] ,HelperFormatMessage(L"%s: %s\n",EWC->GetProperyName(),EWC->GetProperyValue()));

}

И код вызова:

string[] strArr= new string[1];
NativeHelper.ProcessEvent(ref eventRecord, ref strArr);

У меня два вопроса:

  1. Почему, когда я после вызова этой функции проверяю значение переданного массива в c #, я вижу, что он пуст (данные существуют в коде c ++, я его отладил)?

  2. Если я выделяю память в c ++ dll, где мне нужно ее освободить?в c ++ или c #?

Большое спасибо!

Редактировать:

signture c ++:

static __declspec(dllexport) void ProcessEvent(PEVENT_RECORD pEvent, wchar_t** OutPutFormattedData);

Ответы [ 2 ]

1 голос
/ 13 июля 2011

Если вы выделите (управляемый или неуправляемый) буфер (или массив или символы) в C #, а затем заполните C ++:
1. Разблокируйте в C #.
2. Буфер должен быть [in] и не иметь ссылки ref или out в C #.

Если вы выделите буфер (или массив символов) в C ++:
1. Передайте буфер как [out] out IntPtr.
2. Добавьте метод освобождения в подпись C ++.
3. Скопируйте буфер в буфер C # или новую строку
4. Вызовите C ++ deallocater из C # с IntPtr.

Вы можете использовать System.Runtime.InteropServices.Marshal для выделения / освобождения неуправляемой памяти в C #. Не используйте для освобождения памяти, выделенной вне .NET!

System.Runtime.InteropServices.Marshal также можно использовать для копирования памяти из внешнего буфера (IntPtr) в буфер или строку .NET.

Пометить импортированный метод как частный и обернуть открытым (или защищенным или внутренним) методом, который использует параметры .NET (например, который использует System.Runtime.InteropServices.Marshal.Copy и вызывает внешнее освобождение).

C ++:

int ProcessEvent(PEVENT_RECORD eventData, wchar_t*& message)
{
    // example code
    message = new wchar_t[100];
    if (message == 0)
        return 1;
}

void DeallocateString(wchar_t* array)
{
    delete[] arrayPtr;
}

wchar_t* ErrorCodeToMessage(int errorCode)
{
    switch (errorCode)
    {
        case 0: return 0; // return NULL pointer
        case 1: return L"No!!!";
        default: return L"WTF!?";
    }
}

C #:

[DllImport("Helper.dll", EntryPoint = "ProcessEvent")]
private static extern uint ProcessEventExternal(
    [In, Out] ref EventData eventData,
    [In, Out, MarshalAs(UnmanagedType.SysInt))] ref IntPtr resultMessages);

[DllImport("Helper.dll", EntryPoint = "DeallocateString")]
private static extern voidDeallocateStringExternal(
    [In, MarshalAs(UnmanagedType.SysInt)] IntPtr arrayPtr);

[DllImport("Helper.dll", EntryPoint = "ErrorCodeToMessage")]
private static extern
    [return: MarshalAs(UnmanagedType.SysInt)] IntPtr
    ErrorCodeToMessageExternal(int errorCode);

public string ProcessEvent(ref EventData eventData)
{
    IntPtr resultPtr = IntPtr.Zero;
    uint errorCode = ProcessEventExternal(eventData, ref resultPtr);

    if (errorCode != null)
    {
        var errorPtr = ErrorCodeToMessageExternal(errorCode);

        // returns constant string - no need to deallocate
        var errorMessage = Marshal.PtrToStringUni(errorPtr);

        throw new ApplicationException(errorMessage);
    }

    var result = Marshal.PtrToStringUni(resultPtr);
    ExternalDeallocate(resultPtr);

    return result;
}
0 голосов
/ 13 июля 2011

Я не знаю о вопросе 1, но о вопросе 2:

for(int i=0;i<list.Size;i++) 
{
    EventWrapperClass *EWC = new EventWrapperClass();
    EWC = list.Events[i]; 
    ...
}

В первой строке цикла вы создаете новый объект. Во второй строке вы назначаете другому объекту указатель, который хранится в списке list.Events [i]. На этом этапе ничто больше не указывает на ваш первый объект, и у вас есть утечка памяти для каждой итерации цикла!

Измените две строки на

EventWrapperClass *EWC = list.Events[i];

поскольку нет необходимости создавать новый объект.

Затем, если вам больше не нужно событие, удалите его в цикле и обязательно очистите список после этого. Вы можете сделать это, только если вы на 100% уверены, что мероприятие больше не нужно. Это может легко привести к появлению висящих указателей!

...