C#中的封送数组 - 为什么要使用AllocCoTaskMem vs new []?

时间:2017-03-22 15:17:18

标签: c# arrays pinvoke

我正在为我们的项目为C ++设备库创建一个C#facade,这是我的第一个P / Invoke竞技场。请考虑以下声明:

[DllImport(EzSensorEnuMDll, EntryPoint = "VDEIOrM_EnumConnected")]
public static extern int VDEIOrM_EnumConnected(int AMaxEnu, [In,Out] tVDEnu_IOrM[] AEnu_IOrM);

我天真地称这种方法是这样的:

var sensors = new tVDEnu_IOrM[MaxEnumeratedSensors];
var result = VDEIOrM_EnumConnected(MaxEnumeratedSensors, sensors);

它运作得很好。在研究一个不相关的方法调用时,我偶然发现了一个P / Invoke示例,其中程序员调用了一个也接受数组的方法,但他使用了一些我以前不知道的内存分配函数。它看起来像这样:

var buffer = Marshal.AllocCoTaskMem(bufferSize);
MethodCall(buffer);
// do some things
Marshal.FreeCoTaskMem(buffer);

所以我的问题是 - 为什么/什么时候我会使用内存分配函数而不是简单地新建一个数组并传递它?我是否因为纯粹的运气而没有遇到问题?我意识到P / Invoke声明需要更改为接受IntPtr,但有没有理由更喜欢一种方法而不是另一种方法?

1 个答案:

答案 0 :(得分:1)

您的代码看起来很好,没有理由认为它无法正常工作。

最后一个片段对作者为何倾向于这样做提供了很少的见解。 AllocCoTaskMem()从COM互操作堆分配。它的主要作用是允许客户端和服务器代码就他们使用的堆达成一致。例如,当服务器分配需要由客户端释放的内存时,这是必要的。或者相反。

不是这里的情况,客户端都分配和释放内存。就像你一样。请注意,此代码看起来很危险,被调用者无法知道调用者分配了多少内存,因此可以轻松读取或写入缓冲区末尾。如果bufferSize由api合同规定,这只能达到目的。

有一些角落情况可能更适合使用非托管内存:

  • 被调用的函数运行很长时间,并在使用线程的程序中使用。因此,不必固定缓冲区可以有利于GC健康。
  • 被调用函数存储传递的指针,并在函数调用之后使用它。非托管内存的优点是它稳定,其地址不能改变。不太可能在这里,你会看到一个额外的api调用来告诉本机代码停止使用缓冲区。
  • 被调用函数需要使用CoTaskMemRealloc()重新分配缓冲区。绝对不是这里的情况,它将使用ref关键字传递指针。

没有令人信服的理由,这里最好的事情是假设作者不确定如何正确地做到这一点。