问题描述
为了正确起见,如果我对数据流在 C# 程序调用 C++ dll 并将委托作为参数的想象是正确的,我想征求您的意见。
- 系统为 C# 程序提供内存
- C# 程序加载 .dll 并将其部分空间提供给 C++ dll。在这个空间里不会有 C# 垃圾回收,只有卸载 .dll 才能释放整个空间。
- 调用了一个 C++ 函数。特定的函数有一个委托作为参数。我们深入 C++ 内存区域并声明一些变量。 C++ 函数将在其代码中的某处调用 C# 委托。
- C# 委托对 C# 内存进行操作,如果输入参数是本机类型,则在 C# 内存中会有一份其输入参数的副本,或者对 C++ 内存中变量的引用(如果是复杂类型)。如果我们有本机类型,我可以将它保存到 C# 世界中,一切都会好起来的。但是如果它是一个引用并且我只是将它保存到我的 C# 内存中,我会得到未定义的行为,如果我结束我的 C++ 函数,因为变量将超出范围并被销毁。
- C# 函数结束,我们得到 C++ 中的返回值作为副本(或指向返回值的指针,如果是复杂类型,则该指针将指向 C# 内存)
- C++ 函数结束,C++ 函数使用的内存被释放
我这样做对吗?
解决方法
这应该在 marshaller
的文档中描述如果它们是本机类型或对 C++ 内存中变量的引用,如果它是复杂类型。如果我们有本机类型,我可以将它保存到 C# 世界中,一切都会好起来的。但是如果它是一个引用并且我只是将它保存到我的 C# 内存中,我会得到未定义的行为,如果我结束我的 C++ 函数,因为变量将超出范围并被销毁
我的理解是编组器将复杂类型转换为结构或指针 (IntPtr)。结构是按值传递的,因此您将在托管内存中(可能在堆栈上)有一个副本。指针需要不安全的代码才能访问,因此您有责任安全地处理它们。
C++ as copy(或者指向返回值的指针,如果是复杂类型,指针会指向C#内存)
托管函数实际上没有办法以安全的方式返回指向托管内存的指针。要创建指针,您需要修复对象以防止 GC 移动它,但修复是有范围的,因此它不适用于返回值。
我个人认为编组规则有点复杂,我更愿意保持任何 p/Invoke 简单,如果没有其他原因,只是为了避免有关安全的问题。对于 c# 和 c++ 之间更复杂的互操作性,我建议使用 c++/cli。这允许您自己进行类型转换,并添加了大量工具,您可以使用它来确保正确运行。