C++DLL与C#的互操作:公开未知大小的多维数组和内存管理

本文关键字:数组 管理 内存 未知 互操作 C++DLL | 更新日期: 2023-09-27 17:59:23

我有一个C++库,它做一些核心的金融数学(Quantlib)。我有一个针对它构建的普通C++dll,它向C#前端应用程序公开了一个轻量级接口,允许用户传入各种参数、运行场景等。然后dll返回数据以供显示。

然而,我真的不清楚如何处理dll和C#层之间的接口,尤其是我不知道在哪里处理内存分配/释放,即在C#或dll 中

本质上,我希望我的dll返回一个类或结构,该类或结构包含一个2维值数组(double、char*等)。在我调用dll时,我不知道这个数组的大小,所以我想它必须由dll本身分配。

目前,dll返回了一个looooooong char*,其中包含一个由管道分隔的值列表。这似乎有效,但我觉得它不是一个特别优雅的解决方案。

有什么帮助吗,伙计们?

编辑

非常感谢您的反馈。对不起,我已经好几年没有做任何DLL编程了,所以这个问题有点愚蠢。

我决定简单地将2d数组视为一个在C++侧具有高度/宽度偏移的1d数组,并直接猜测数组的大小。在C#端创建一个2d数组,并以这种方式将其编组到C++似乎效果不错。

这是我的东西。有什么想法吗?

C#:

    [DllImport(<dllPath>, CallingConvention = CallingConvention.Cdecl)]
    static extern void ChangeArray2d(double[,] arr, int l1, int l2);
    //populated with some sample values
    double[,] arr = new double[,] { { 1, 2, 3, 4 }, { 5, 6, 7, 8 } };
    ChangeArray2d(arr, arr.GetLength(0), arr.GetLength(1));

C++:

 __declspec(dllexport) void ChangeArray2d( double* arrayin, int height, int width)
{
    for (int n = 0; n<height; n++){
        for (int m = 0; m<width; m++){
            arrayin[n*width+m]  = arrayin[n*width+m] + 100;
        }
    };
    return;
}

C++DLL与C#的互操作:公开未知大小的多维数组和内存管理

通常情况下,分配缓冲区,将其(缓冲区大小为您一次可以处理的最大数据量)传递给C++本机函数,由其填充缓冲区,然后处理数据/释放缓冲区是常见的模式。或者重复使用缓冲区。

如果您的本机函数为您分配了缓冲区,然后您应该释放它——这是一个不同的脆弱契约,也禁止内存重用。例如,如果您需要调用此函数20次,则会有20次不必要的分配。

类似的东西:

private int YourNativeFunctionStub(IntPtr buffer, int bufferSize)
{
    int writtenToBufferBytes = bufferSize; // if it wrote less to buffer - it should return correct count of bytes
    // here your library fills the buffer with data
    return writtenToBufferBytes;
}
private double[] GetArrayFromNative()
{
    int bufsize = 1024; // probably you should find it by calling another func from your library?
    IntPtr nativeBuffer = Marshal.AllocHGlobal(bufsize);
    try
    {
        int bytesReceivedInBuffer = YourNativeFunctionStub(nativeBuffer, bufsize);
        int receivedArrayLength = bytesReceivedInBuffer / sizeof(double);
        var managedArray = new double[receivedArrayLength];
        Marshal.Copy(nativeBuffer, managedArray, 0, receivedArrayLength);
        return managedArray;
    }
    finally
    {
        Marshal.FreeHGlobal(nativeBuffer);
    }
}

UPD:参见汉斯的评论。