从 C++ 到 C# 的数组

Posted

技术标签:

【中文标题】从 C++ 到 C# 的数组【英文标题】:Array from C++ to C# 【发布时间】:2016-03-25 16:41:10 【问题描述】:

我试图将一个双数组(它实际上是一个 std::vector,但在传输时转换)从一个 c++ dll 传递到一个 c# 脚本(统一)。 使用此处列出的方法https://***.com/a/31418775。

我可以成功地在我的控制台上统一打印数组的大小,但是我无法使用“CoTaskMemAlloc”为数组分配内存,因为我使用的是 Xcode 并且它似乎没有 COM。

对于更多背景知识,此数组是 GUI 控件的一部分,c++ 创建它,并且用户使用 c# GUI 进行编辑 - 因此计划是能够在数组被编辑后将其传递回 c++ .

C++ code
extern "C" ABA_API void getArray(long* len, double **data)
    *len = delArray.size();
    auto size = (*len)*sizeof(double);
    *data = static_cast<double*>(CoTaskMemAlloc(size));
    memcpy(*data, delArray.data(), size);


C# code 
[DllImport("AudioPluginSpecDelay")]
private static extern void getArray (out int length,[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] out double[] array);

int theSize;
double[] theArray;
getArray(out theSize, out theArray);

如果我省略了有关数组的代码,int 就可以通过。所以我相信这个方法是正确的,它只是解决了 CoTaskMemAlloc 的不足。

【问题讨论】:

你是说你不确定在 c++ 端,或者在 c# 端做什么?问题出在哪里? 嘿,问题出在为数组分配内存的 CoTaskMemAlloc 上(它必须是 CoTaskMemAlloc,我尝试了其他内存分配但它没有工作)。它在mac上不存在!正如 yms 在下面指出的那样,GLib 应该是 mac 的替代品。我对其他不使用 GLib 的方法持开放态度 :) 你也可以在 C# 端做所有的内存管理:) 嗨@k9256 - 我想问你一些事情。只是 FWIW,你到底想做什么?仅供参考:在 Unity 中非常常见的是:经验丰富的程序员(比如你自己)在玩 Unity,“不小心”走上了尝试做某事的道路,却没有意识到有一种非常简单的方法可以实现 说Unity 环境中的事情。就像好奇一样,您实际上想做什么?干杯 嘿乔,我目前正在使用统一原生音频 SDK 制作频谱延迟音频插件。 c++/c# 之间的数组是延迟时间数组,因此用户可以使用 GUI 为 FFT(在 C++ 中)的每个 bin 选择不同的时间。这是我第一次使用 SDK,而且我还没有经常使用 C++。因此,同时学习两者的最佳实践是一项挑战! 【参考方案1】:

您应该能够在 XCode 中使用 malloc 分配内存,并在 C# 中使用 Marshal.FreeCoTaskMem 释放它。但是,为了能够释放它,您需要拥有 IntPtr:

C++ 代码

extern "C" ABA_API void getArray(long* len, double **data)

    *len = delArray.size();
    auto size = (*len)*sizeof(double);
    *data = static_cast<double*>(malloc(size));
    memcpy(*data, delArray.data(), size);

C#代码

[DllImport("AudioPluginSpecDelay")]
private static extern void getArray(out int length, out IntPtr array);

int theSize;
IntPtr theArrayPtr;
double[] theArray;

getArray(out theSize, out theArrayPtr);
Marshal.Copy(theArrayPtr, theArray, 0, theSize);
Marshal.FreeCoTaskMem(theArrayPtr);

// theArray is a valid managed object while the native array is already freed

编辑

从Memory Management 我了解到Marshal.FreeCoTaskMem 最有可能使用free() 实现,因此合适的分配器将是malloc()

有两种方法可以确定:

    在 CLI 中使用 Marshal.AllocCoTaskMem 分配内存,将其传递给本机进行填充,然后在 CLI 中再次使用 Marshal.FreeCoTaskMem 释放它。 保持原样(本机使用malloc() 分配内存),但不要在 CLI 中释放内存。相反,使用另一个本机函数,如 freeArray(double **data),并在 CLI 完成使用它后为您提供 free() 数组。

【讨论】:

这绝对是完美的,我太沉迷于完美地遵循另一种方法来考虑这样做!谢谢。 @k9256 我不确定这是否正确...您将使用一个函数进行分配并使用不同的(可能不相关的)函数进行取消分配。至少尝试确保您的代码没有泄漏内存,即使它看起来像“它可以工作” 刚刚检查过,当我使用自己制作的 GUI 时,Unity 的内存使用量确实会上升。我尝试在 C++ 中添加 free(*data),但这会导致崩溃。您对在哪里寻找阻止内存泄漏的建议有什么建议吗? 我现在不确定内存泄漏,Unity 似乎在没有我的插件存在的情况下自行爬升。 Marshal.FreeCoTaskMem(arrayPtr) 不会释放 C++ 分配的内存吗?我知道我应该在 C++ 中使用 CoTaskMemAlloc,但我并不完全清楚区别 由于第一种方法是在 CLI 中释放内存,因此“正确”编程 - 我理解为 newdelete - 将不起作用,因为它们包含额外的逻辑CLI 不知道。因此,解决方案必须仅限于普通内存分配。随时发布提出不同方法的答案。【参考方案2】:

我不是 Unity 方面的专家,但似乎 Unity relies on Mono for it's C# scripting support。看看这个文档页面:

Memory Management in Mono

我们可以从那里假设您需要在 C++ 端使用平台相关的代码,您需要在 Windows 中使用 CoTaskMemAlloc/CoTaskMemFree,在 Unix 中使用 GLib 内存函数 g_malloc() 和 g_free()(如 iosandroid 等)。

如果您可以控制所有代码(C++ 和 C#),实现此功能的最简单方法是在 C# 脚本中执行所有内存分配/解除分配。

示例代码(未经测试):

//C++ code

extern "C" ABA_API long getArrayLength()
    return delArray.size();


extern "C" ABA_API void getArray(long len, double *data)
    if (delArray.size() <= len)
        memcpy(data, delArray.data(), delArray.size());


// C# code
[DllImport("AudioPluginSpecDelay")]
private static extern int getArrayLength(); 

[DllImport("AudioPluginSpecDelay")]
private static extern void getArray(int length,[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] double[] array);

int theSize = getArrayLength();
double[] theArray = new double[theSize];
getArray(theSize, theArray);

【讨论】:

嘿,感谢您的文档链接,我现在正在尝试获取 GLib。我对 C++ 不是很有信心,所以我希望我不必这样做,但这似乎是不可避免的!

以上是关于从 C++ 到 C# 的数组的主要内容,如果未能解决你的问题,请参考以下文章

从 C++ 到 C# 的数组

将安全数组的安全数组从 C++ 中的 VARIANT 编组到 C#

将二维数组从 C# 传递到 C++

将结构数组从 C# 传递到 C++

如何将二维数组从 C# 传递到 C++?

将字节数组从 c++ 传递到 c# 程序集都有哪些不同的方法?