Маршал 2D массив структур из C # в родной C ++ - PullRequest
2 голосов
/ 21 октября 2011

Мне нужно зайти в C ++ из C # и вернуть 2D-массив структуры.У меня все настроено, и если я присоединяю отладчик, кажется, что все идет хорошо, за исключением того, что мой 2D-массив не выполняет маршалинг должным образом.Если я загружаю его значениями перед вызовом нативного метода, а затем просматриваю массив с нативной стороны, я получаю множество «недопустимых» указателей в моем окне просмотра в VS.Затем код C ++ идет вперед и прекрасно загружает массив со значениями, но во время маршалинга обратно в C # я получаю нарушение доступа к памяти.

Я бы предпочел не делать это как массив 1d.

Вот мое определение структуры и метода C ++:

struct DoubleStringStruct
{
    BSTR Value;
    BSTR NumberFormat;
};

HRESULT WINAPI NativeArrayHandler(LONG rMax, LONG cMax, DoubleStringStruct** values)
{
    for(LONG rn=1; rn <= rMax; rn++)
    {
         for (LONG cn = 1; cn <= cMax; cn++)
         {
               DoubleStringStruct s;
               s.Value = _wcsdup(L"Test");
               s.NumberFormat = _wcsdup(L"Test");
               values[rn][cn] = s;
         }
     }

     return S_OK;
}

и мой код C #:

[StructLayout(LayoutKind.Sequential)]
public struct DoubleStringStruct
{
    [MarshalAs(UnmanagedType.BStr)]
    public string value;
    [MarshalAs(UnmanagedType.BStr)]
    public string numberFormat;
}


[System.Runtime.InteropServices.DllImport(c_dllName)]
public static extern void NativeArrayHandler(int hMax, int cMax, DoubleStringStruct[,] args);

public void sometMethod()
{
     DoubleStringStruct[,] someDSS= new DoubleStringStruct[4,3];

     NativeArrayHandler(4, 3, someDSS);
}

1 Ответ

0 голосов
/ 22 октября 2011

Хм, хорошо, Ханс Пассант помог мне добраться до этого ответа, поэтому поддержите его.

С моим кодом было три проблемы

1) _wcsdup возвращает WCHAR_T *, но моя структура содержитBSTR, который на самом деле является WHCAR *

2) Маршаллер не создает для нас 2d-массив, а скорее 1-мерный, который нужно забавным образом проиндексировать.Примечание ниже.

3) Мне нужно убедиться, что любая память, которую я создаю в нативном коде, очищается либо мной, либо самим Маршаллером.Например, почти вся собственная память, которую я использовал в этом вопросе, никогда не освобождается, что приводит к огромной утечке памяти.В настоящее время, когда нативный код возвращается к управляемому коду, я теряю все свои нативные указатели на память, которую необходимо освободить.Я решил эту передачу обратного вызова при выполнении вызова в нативный код.Нативный код выполняет свою работу, выполняет функциональный обратный вызов управляемому, который возвращает и позволяет нативному коду выполнять служебную работу.Самый простой способ сделать это - использовать возможности CComSafeArray и CComBSTR, которые будут управлять собой.(Я знаю, что смогу просто передать CComSafeArray маршаллеру, и они будут очищены в коде .net, но я не смог понять, как это сделать).

К сожалению, маршалинг 2D-массива требует пользовательского маршалинга, что приводит к слишком большому количеству COM-вызовов на мой вкус.Следовательно, я собрал 1D массив и проиндексировал его в соответствии с предложением Ганса Пассанта.Более того, из-за нехватки времени я создал массив для каждой строки в DoubleStringStruct, хотя я мог бы сделать DoubleStringStruct COMVisible, а затем я мог бы упорядочить его в одном массиве.

Вот последний код, с которым я закончил.

extern "C" __declspec(dllexport)
HRESULT WINAPI NativeArrayHandler(LONG rMax, LONG cMax, void (WINAPI*callback)(SAFEARRAY*, SAFEARRAY*))
{
    CComSafeArray<BSTR> valuesArr = CComSafeArray<BSTR>(rMax*cMax);
    CComSafeArray<BSTR> formatsArr = CComSafeArray<BSTR>(rMax*cMax);


    for(LONG rn=0; rn < rMax; rn++)
    {
         for (LONG cn = 0; cn < cMax; cn++)
         {
               int index = cMax * rn + cn;
               valuesArr[index] = CComBSTR(L"Test");
               formatsArr[index] = CComBSTR(L"Test");
         }
     }

    callback(valuesArr, formatsArr);

    valuesArr.Destroy();
    formatsArr.Destroy();

    return S_OK;
}

И C #

static void Main(string[] args)
{
        NativeArrayHandler(4, 3, (v, f) => { printArrays(4, 3, v, f); });
}


public static void printArrays(int rmax, int cmax, string[] valuesArr, string[] formatsArr)
{
     // can print the arrays in managed code here
}


    [System.Runtime.InteropServices.DllImport("dll location")]
    public static extern void NativeArrayHandler(int hMax, int cMax, NativeArrayHandlerCallback cb);


    public delegate void NativeArrayHandlerCallback(
        [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_BSTR)] string[] arr1,
        [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_BSTR)] string[] arr2);
...