如何在 C++ 中构建动态数组并将其返回到 C#/.NET return

How to build a dynamic array in C++ and return it back to C#/.NET

我必须找到在 C++ Win32 端构建结构数组的方法。我没有项目的初始数量。调整该数组的大小应该非常快。

构建列表后,我需要 return 将其返回到 .NET。因此,该数组(列表)应该转换为可以在 .NET 端轻松读取的传输,或者可以使用初始列表 "as is" 只需传递指针。

提前感谢您给我提示!

通常,如果您的 C++ dll 编译时支持 CLI,您可以简单地使用托管容器。如果您不希望使用 CLI 选项编译您的 dll,那么您可以编写小型 C++/CLI 包装器 dll,它将调用本机 C++ dll 中的方法并将对象存储在托管容器中。 另一种可能的解决方案是通过索引将 C++ 库的接口更改为 return 对象,支持插入 and/or 删除。

std::vector<CFoo> vec;

void init() {
    //read data to vec
}

CFoo getIthElement(int i) {
    return vec[i];
}

int getElementCount() {
    return vec.size();
}

因此您将在 C# 端使用 getIthElementgetElementCount 函数。

在 C++ 中实现 "dynamic arrays" 的一种非常常见的方法是使用 STL 的 std::vector。在您的情况下,您可以定义 vector<SomeData>std::vector 可以根据您的要求 动态地 更改其大小(即在 运行 时间):您可以使用其 push_backemplace_back 方法,将新项目添加到向量中。

但是,C# 没有 "understand" std::vector

要将其内容编组到 C#,您可以使用 CLR 非常理解的 SAFEARRAY。 SAFEARRAY 的好处在于,除了数组内容之外,它还存储数组大小:它是一个独立的数据结构。 因此,您可以创建一个适当大小的 SAFEARRAY,用向量中动态创建的内容填充它,然后将该 SAFEARRAY 传递给 C#。

请注意,您也可以直接构建 SAFEARRAY,而无需传递给 std::vector。但是STL的vector有更强大的编程接口;因此,如果您想在 运行 时通过 push_back 或 emplace_back 添加多个项目,请先构建 std::vector,然后 "marshalling" 将其构建为SAFEARRAY,可能是更好的选择。无论如何,这取决于您的特定需求和背景。


作为替代方案,您可以使用 C++/CLI 作为 C++ 端和 C# 端之间的桥接层。在这种情况下,您可以使用 gcnew 创建 .NET "managed" array.


并且,作为另一种选择,您可以使用 C 接口从本机 C++ DLL 中导出几个函数:

  • 获取数组中项目数的函数

  • 另一个函数,用于获取输出调用者分配的缓冲区中的实际数组数据(其大小由前一个函数返回)

在 C# 中,使用 IntPtr 输出参数传递输出缓冲区指针。
然后,您可以使用 Marshal.PtrToStructure 编组单个数据项,并且可以使用 Marshal.SizeOf 增加指针,使其指向数组中的下一项。

像这样:

// In C++:

struct SomeData
{
    /* your data fields */
};

// Export these two functions from your native DLL:
extern "C" int GetSomeDataCount( /* some params */ );
extern "C" void GetSomeData( SomeData* ptr, /* some other params */ );


// In C#:    

public struct SomeData
{
    // Map C++ data structure fields to C#
}

static extern int GetSomeDataCount( /* some params */ );
static extern void GetSomeData( [Out] out IntPtr ptr, /* some other params */ );

SomeData[] GetSomeData( /* some params */ )
{
    // Allocate an array of proper size
    SomeData[] dataArray = new SomeData[ GetSomeDataCount( /* some params */ ) ];

    // Fill the array with content from GetSomeData
    IntPtr dataPtr;
    GetSomeData( out dataPtr, /* some other params */ );
    for (int i = 0; i < dataArray.Length; i++)
    {
        dataArray[i] = Marshal.PtrToStructure(dataPtr, typeof(SomeData));
        dataPtr += Marshal.SizeOf(typeof(SomeData));
    }
    return dataArray;
}