在 C# 中封送数组 - 为什么使用 AllocCoTaskMem 与 new []?

Marshaling arrays in C# - why use AllocCoTaskMem vs new []?

我正在为我们项目的 C++ 设备库创建 C# 外观,这是我的第一个 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);

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

您的代码看起来不错,没有理由认为它不能工作。

最后一个片段几乎没有说明为什么作者喜欢这样做。 AllocCoTaskMem() 从 COM 互操作堆分配。它的主要作用是允许客户端和服务器代码就它们使用的堆达成一致。必要时,比如说,服务器分配需要由客户端释放的内存。或者反过来。

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

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

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

没有令人信服的理由,这里最好假设作者不确定如何正确执行此操作。