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