P/Invoke 访问的引脚数据

tel*_*dor 3 c# c++ clr pinvoke

我使用 P/Invoke 将数据从 C# 代码传递到 C++ 代码,反之亦然。到目前为止效果很好。

最近我读了几篇文章(例如这篇需要固定这些数据的

我查阅了一些微软的文章,但对我来说,它们并不完全清楚何时需要手动完成固定。我对这篇文章的理解是,CLR 确保 GC 收集时不会出现问题。它通过固定数据或将数据复制到 GC 不收集的非托管内存中来实现这一点。所以对我来说这意味着程序员不需要关心固定。本文中的示例也没有显示任何固定。我仍然不确定我的结论是否正确。

进一步挖掘,我发现了我在代码中使用的特定数据类型的更多信息:

int, long:按值传递 - 因此不得固定。但是关于ref int

IntPtr:我使用 AllocHGlobal() 在非托管内存中分配空间。GC 不涉及这一点。因此无需固定。

byte[]:通过引用传递但自动固定。请参阅此处作为一种优化,可直接传送类型的数组和仅包含可直接传送成员的类的数组将在封送过程中固定而不是复制。

string:通过引用传递但自动固定。请参阅此处在对 String 等对象进行封送期间会自动执行固定,但您也可以使用 GCHandle 类手动固定内存。

string[], 'custom struct with strings': 这里我真的不确定。“在对诸如 String [...] 之类的对象进行编组期间自动执行 Pinning ”这句话是否包含字符串数组和自定义结构?

目前我没有固定任何东西,代码工作正常。即使我在 C++ 执行其任务时强制 GC 进行收集。但这当然并不意味着它总是工作得很好。我使用.Net Framework 4.8。

我需要固定上述数据类型吗?

Mar*_*ell 5

这里的规则取决于您所调用的内容,因此在没有具体示例的情况下只能给出模糊的建议,但是:

如果您通过 P/Invoke 调用的方法仅在该 P/Invoke 调用期间使用指针,那么:在几乎所有情况下,您都可以传递隐式指针,或者在fixed调用周围使用(即,如果P/Invoke 签名只需声明SomeStruct*IntPtr),就可以正常工作。

如果您通过 P/Invoke 调用的方法在返回之前存储指针,然后期望该指针有意义(可能对于基于回调的异步/完成 API),那么这就是您需要确保的问题场景数据不会被移动:

  • 对于非托管内存,例如 from AllocHGlobal:没有什么必要
  • 对于堆栈下方的内存(即在此期间堆栈帧不会被重用):没有什么必要
    • (如果您在 P/Invoke 中使用堆栈内存并且无法满足该条件:您犯了严重的设计错误)
  • 对于托管内存(堆上的对象等);这就是乐趣的开始;请注意,.NET 5 引入了“固定对象堆”(通常用于用于 P/Invoke 的数组),但除此之外:您需要手动固定