如何在c++中传递c#对象引用
本文关键字:对象引用 c++ | 更新日期: 2023-09-27 18:08:59
是否有任何方法通过本地互操作将c#对象引用(类类型,而不是结构)传入和传出c++ ?
这是我的用例:
我试图在c#中编写一个游戏引擎,但想使用本地(c++)物理库。物理库维护所有物理体的内部状态,并允许用户将少量数据(指针)与每个体相关联。在物理模拟标记之后,库提供了所有移动的物理物体的列表。我想遍历这个列表,并将所有更改传递给相应的c#对象。
问题是:将c#中的对象与c++中相应的物理实体相关联的最佳方式是什么?我可以想到几个方法:
- 给每个物理实体一个某种类型的ID,在c++中将ID与物理实体关联起来,并在c#中维护ID到相应对象的映射。在我看来,这似乎是不必要的间接,即使有一个优化的映射机制。利用将c#委托(可以隐式引用c#对象实例)编组到c++函数指针中的能力。这可能是最好的方法;我不知道c#是如何实现委托的,也不知道这会带来什么样的开销。
- 在c++中找到引用c#对象的其他方法,并将该引用与每个本地物理实体相关联。
有没有类似选项3的机制是我不知道的?c++/CLI不是一个选择,因为我想支持没有它的平台。
我建议使用专门为这种情况设计的工具,即System.Runtime.InteropServices.GCHandle
。
:
- 用
GCHandleType.Normal
或GCHandleType.Weak
为CLR对象分配GCHandle
。 - 通过
GCHandle.ToIntPtr
将GCHandle
转换为IntPtr
。 - 将
IntPtr
作为您提到的userdata指针传递给c++端。
:
- 从c++端返回
IntPtr
- 通过
GCHandle.FromIntPtr
将IntPtr
转换回GCHandle
- 使用
GCHandle
的Target
属性来获取原始CLR对象
当CLR对象不再从c++端被引用时(要么是因为c++对象被销毁,要么是因为你将userdata指针重置为例如IntPtr.Zero
),通过调用GCHandle.Free
来释放GCHandle
。
应该分配哪种类型的GCHandle
取决于您是否希望GCHandle
保持对象存活(GCHandleType.Normal
)或不存活(GCHandleType.Weak
)。
- 映射唯一标识符将允许您将托管代码与非托管代码隔离开来。如果非托管端不对托管对象做任何事情可能会更好,因为你真的无法控制对象的生命周期。
- 委托作为函数指针是可能的。您所需要做的就是定义委托类型并创建实例。然后,您可以使用
Marshal.GetFunctionPointerForDelegate
获取一个地址以传递给非托管端。唯一需要注意的是,你必须确保回调是使用__stdcall
。 直接访问类成员和函数是可能的,但是如果没有c++/CLI包装器,你就是在玩火。 - 在c#端分配一块内存并将其传递给c++。在托管端的类中,使用属性以适当的偏移量访问该内存。
- 使COM对象可见。
另外,请记住,AccessViolationException
通常在。net中是不可捕获的。