从C#到本机C ++的Marshal 2D数组结构

时间:2011-10-20 23:10:12

标签: .net marshalling

我需要从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 :(得分:0)

嗯,Hans Passant帮助我得到了这个答案,所以对他有道具。

我的代码有三个问题

1)_wcsdup返回WCHAR_T *,但我的结构包含BSTR,这实际上是一个WHCAR *

2)marshaller不会为我们创建一个二维数组,而是一个必须以有趣的方式编入索引的一维数组。请注意以下内容。

3)我需要确保我在本机代码中创建的任何内存都会被我自己或Marshaller清理干净。例如,几乎所有我在问题中使用的本机内存永远不会被释放,导致巨大的内存泄漏。目前,当本机代码返回托管代码时,我丢失了所有需要释放的内存指针。我在调用本机代码时解决了这个问题。本机代码完成其工作,将函数调用回托管,返回,并允许本机代码进行内务处理。一个简单的方法就是使用CComSafeArray和CComBSTR的功能来管理自己。 (我知道我应该能够简单地将CComSafeArray传递给编组器,他们将在.net代码中清理,但我无法弄清楚如何做到这一点。)

不幸的是,二维数组的编组需要自定义编组,这导致我的口味过多的COM调用。因此,根据Hans Passant的建议,我整理了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);