使用DLLImport将数据发送到非托管代码

时间:2014-02-09 06:27:06

标签: c# c++ interop

我的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);

1 个答案:

答案 0 :(得分:3)

没有。该副本迟早必须再次发布。 pinvoke marshaller无法知道本机代码使用哪个分配器。因此它无法可靠地选择堆来存储副本。您可以编写以强制复制的唯一[MarshalAs]是使用UnmanagedType.SafeArray的。为使互操作安全而创建的特定数组类型,通常仅由显式编写为支持互操作的代码接受。就像COM代码一样,你通常不会这样做。

有些情况下,必须制作副本,当托管数据不是blittable并需要按照[StructLayout]的指示从托管布局转换为本机布局时发生。然而,这完全是透明的,并且编组人员将在呼叫完成后始终删除副本。

可以通过在C#代码中显式创建副本来使其正常工作。您将参数声明为IntPtr并使用Marshal.AllocHGlobal()或Marshal.AllocCoTaskMem()分配内存。并自己编组数据,通常通过调用Marshal.StructureToPtr()除非你知道本机代码使用适当的方式再次释放副本这一事实,否则这种情况并不好。换句话说,LocalFree()或CoTaskMemFree()。如果您使用VS2012或更高版本的本机代码,则赔率显着增加,其CRT现在使用默认进程堆而不是创建自己的堆。