我的C ++代码中有以下签名
extern "C" __declspec(dllexport) void*__cdecl
widgetCreate(char* data, size_t length){
return new Widget(data);
}
以下是我的C#代码:
[DllImport(Path, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr widgetCreate(byte[] data, int length);
我希望c ++代码保留自己的data
副本,所以我做memcpy()
:
extern "C" __declspec(dllexport) void*__cdecl
widgetCreate(char* data, size_t length){
auto copy = new char[length];
memcpy(copy, data, length);
return new Widget(copy);
}
有没有办法让编组人员为我复制该副本?像下面这样的东西?
[DllImport(Path, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr widgetCreate([CopyThisThing] byte[] data);
答案 0 :(得分:3)
没有。该副本迟早必须再次发布。 pinvoke marshaller无法知道本机代码使用哪个分配器。因此它无法可靠地选择堆来存储副本。您可以编写以强制复制的唯一[MarshalAs]是使用UnmanagedType.SafeArray的。为使互操作安全而创建的特定数组类型,通常仅由显式编写为支持互操作的代码接受。就像COM代码一样,你通常不会这样做。
有些情况下,必须制作副本,当托管数据不是blittable并需要按照[StructLayout]的指示从托管布局转换为本机布局时发生。然而,这完全是透明的,并且编组人员将在呼叫完成后始终删除副本。
您可以通过在C#代码中显式创建副本来使其正常工作。您将参数声明为IntPtr并使用Marshal.AllocHGlobal()或Marshal.AllocCoTaskMem()分配内存。并自己编组数据,通常通过调用Marshal.StructureToPtr()除非你知道本机代码使用适当的方式再次释放副本这一事实,否则这种情况并不好。换句话说,LocalFree()或CoTaskMemFree()。如果您使用VS2012或更高版本的本机代码,则赔率显着增加,其CRT现在使用默认进程堆而不是创建自己的堆。