【发布时间】:2020-12-25 18:54:57
【问题描述】:
我正在使用 P/Invoke 将数据从 C#-Code 传递到 C++-Code,反之亦然。到目前为止,这工作正常。
最近我读了几篇关于需要固定这些数据的文章(例如this one),因为 GC 可能会在 C++ 执行其任务时重新排列或删除它们。
我查阅了一些 Microsoft 文章,但对我而言,它们并不完全清楚何时需要手动完成固定。我理解this 文章的方式是CLR 确保GC 收集时不会出现任何问题。它通过固定数据或将它们复制到 GC 不收集的非托管内存中来做到这一点。所以对我来说,这意味着程序员不需要处理固定。 article 中的示例也没有显示任何固定。我仍然不确定我的结论是否正确。
进一步挖掘,我发现了一些关于我在代码中使用的特定数据类型的更多信息:
int, long:按值传递 - 因此不得固定。但是ref int呢?
IntPtr: 我使用 AllocHGlobal() 在非托管内存中分配空间。 GC 没有涉及到这一点。所以不需要固定。
byte[]:通过引用传递但自动固定。请参阅here:作为一种优化,仅包含 blittable 成员的 blittable 类型和类的数组在编组期间被固定而不是复制。
string:通过引用传递但自动固定。请参阅here:在编组对象(如字符串)期间自动执行固定,但是您也可以使用 GCHandle 类手动固定内存。
string[],'custom struct with strings':这里我真的不确定。句子“Pinning is automatically executed during marshaling for objects such as String [...]”是否包括字符串数组和自定义结构?
目前我没有固定任何东西,代码工作正常。即使我在 C++ 执行任务时强制 GC 收集。但这当然并不意味着它总是能正常工作。 我使用 .Net Framework 4.8。
我需要对上述数据类型进行固定吗?
【问题讨论】:
-
啊,博客文章。它在描述您需要在哪种情况下执行此操作时做得很差。这是非常少见的,一个需要它的 api 在非托管代码中与 pinvoke 一样难以使用和危险。仅当非托管代码存储您传递的指针时,才需要显式固定。你会期望一个 initialize 函数,一个或多个从你传递的数据中转换或提取信息的实用函数,以及(希望)一个 release 函数来表明你是不再调用实用程序函数。
-
感谢您的快速回复。所以如果我只在函数调用期间使用传递的指针,那么我不需要固定它们?
-
没错。