【问题标题】:Marshal 2D array of structs from C# to native C++将 2D 结构数组从 C# 编组到本机 C++
【发布时间】:2011-10-20 23:10:12
【问题描述】:

我需要从 C# 进入 C++ 并带回一个结构的二维数组。我已经设置好了所有东西,如果我附加了一个调试器,一切似乎都正常,除了我的 2D 数组没有正确编组。如果我在调用本机方法之前用值加载它,然后从本机端查看数组,我会在 VS 的监视窗口中得到很多“无效”指针。然后 C++ 代码继续执行并加载带有值的数组就好了,但是在封送回 C# 期间,我遇到了内存访问冲突。

我宁愿不把它作为一维数组来做。

这是我的 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);
}

【问题讨论】:

  • FWIW - 我注意到在本机代码中,数组大小参数正确传递。
  • 尝试使用数组数组 ([][]) 而不是多维数组 ([,]),看看是否可行。
  • 嗯...直到我看到这个人,我才认为这是可能的,但我希望能有更多“自动”的东西stackoverflow.com/questions/6327196/…

标签: .net marshalling


【解决方案1】:

嗯,Hans Passant 帮助我得到了这个答案,所以支持他。

我的代码存在三个问题

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

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

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

不幸的是,2D 数组的封送处理需要自定义封送处理,这导致我需要太多的 COM 调用。因此,我按照 Hans Passant 的建议编组了一个一维数组并相应地对其进行了索引。此外,由于时间限制,我为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);

【讨论】:

    猜你喜欢
    • 2016-10-09
    • 2010-12-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多