当 C# 调用 c++ 函数时,是不是可以在被调用的 c++ 函数中初始化数组并返回到 C#?
Posted
技术标签:
【中文标题】当 C# 调用 c++ 函数时,是不是可以在被调用的 c++ 函数中初始化数组并返回到 C#?【英文标题】:when C# call a c++ function, is it possible to initialize array in called c++ function and back to C#?当 C# 调用 c++ 函数时,是否可以在被调用的 c++ 函数中初始化数组并返回到 C#? 【发布时间】:2016-11-03 10:17:47 【问题描述】:c++ 函数知道数组的大小并且必须创建它。
c++ 代码:
__declspec(dllexport) int getlog(int* &data)
data = new int[5];
for (int i = 0; i < 5; i++)
data[i] = i;
return 0;
和c#:
[DllImport(@"myDll.dll", CallingConvention=CallingConvention.Cdecl)]
public static extern int getlog(int[] data);
和
int[] arr = null;
getlog(arr);
我看到了this,但没有帮助
【问题讨论】:
Returning an int array in C++/CLI to c# .NET的可能重复 @StoryTeller 提到了与问题相关的链接,但不是重复的。在链接下,通过gcnew
在 C++/CLI 端分配的内存。这不是 OP 要求的。
感谢 StoryTeller 和 Nikita。 win32project 和 CLI 有区别吗?我需要一些参数中的数组而不是返回值
【参考方案1】:
在 C# 方面:
[DllImport(@"myDll.dll", CallingConvention=CallingConvention.Cdecl)]
public static extern int getlog([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] out int[] data,
out int length);
在 C++ 方面:
#include <Objbase.h>
extern "C" __declspec(dllexport) int getlog(int** data, int * length)
*length = 5;
int sizeInBytes = sizeof(int) * (*length);
*data = static_cast<int*>(CoTaskMemAlloc(sizeInBytes ));
for (int i = 0; i < 5; i++)
(*data)[i] = i;
return 0;
CoTaskMemAlloc
将使 CLR 能够在内存不再可访问时负责释放内存。更多信息请关注"Memory Management with the Interop Marshaler":
运行时总是使用
CoTaskMemFree
方法来释放内存。如果您正在使用的内存不是通过CoTaskMemAlloc
方法分配的,您必须使用IntPtr
并使用适当的方法手动释放内存。
上述方法适用于 .NET 4.0 及更高版本。对于 .NET 3.5,由于 unmanaged arrays 的支持有限,它会失败:
被控制的数组不能作为 ref 或 out 参数传递。同样,包含数组大小的参数必须通过值传递(SizeParamIndex 字段不能引用 ref 或 out 参数)
要克服这个问题并在 C# 端获取数组,请使用以下签名:
[DllImport(@"MyDll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int getlog(out IntPtr data, out int length);
当您有IntPtr
到数据时,可以将内容复制到托管数组:
int length;
IntPtr data;
getlog(out data, out length);
int[] managedArr = new int[length];
Marshal.Copy(data, managedArr, 0, length);
您也可以处理此类数组的内容而无需应对。要使用此方法,请在项目设置中启用“允许不安全代码”:
int length;
IntPtr data;
getlog(out data, out length);
unsafe
int* intArray = (int*)data.ToPointer();
for (int i = 0; i < length; i++)
Console.WriteLine(intArray[i]);
【讨论】:
@mohsen 解决了您的问题还是需要更多帮助? 谢谢@Nikita,抱歉。我尝试此代码并得到 Cannot marshal 'parameter #1': Cannot use SizeParamIndex for ByRef array parameters 错误。 @mohsen 您是否尝试过答案中的 C# 和 C++ 代码?我刚刚运行它并成功获得了 C# 端的缓冲区。我使用以下代码在 C# 中调用该方法:int len; int[] data; getlog(out data, out len);
谢谢@Nikita。这很好!不幸的是,我不能投票给答案
@user1000247 是的,您应该手动释放内存。使用Marshal.FreeCoTaskMem(data);
以上是关于当 C# 调用 c++ 函数时,是不是可以在被调用的 c++ 函数中初始化数组并返回到 C#?的主要内容,如果未能解决你的问题,请参考以下文章
C# 调用C++ DLL,而c++函数的有一个参数需要是null,该怎么传递?