一般是通过gcroot<>
或者auto_gcroot<>
来完成这个工作.但也可以直接用GCHandle
来实现。
其实,gcroot<>
本身也是这么实现的。
using namespace System;
using namespace System::Runtime::InteropServices;
#pragma managed
class StringWrapper { //native class
IntPtr m_handle;
public:
StringWrapper() {
String ^ str = gcnew String("ManagedString");
m_handle = static_cast<IntPtr>(GCHandle::Alloc(str));
}
~StringWrapper() {
static_cast<GCHandle>(m_handle).Free();
}
void PrintString() {
// Target是Object^,在此拆箱
String ^ targetStr = safe_cast<String^>(static_cast<GCHandle>(m_handle).Target);
Console::WriteLine("StringWrapper::m_handle == {0}", targetStr);
}
};
#pragma unmanaged
int main() {
StringWrapper s;
s.PrintString();
}
GCHandle和IntPtr有默认和显式的转换函数:
public static explicit operator GCHandle(IntPtr value);
public static GCHandle FromIntPtr(IntPtr value);
public static explicit operator IntPtr(GCHandle value);
这是因为GCHandle每次Alloc后,会将新的GCHandle的地址记录在内部的静态s_cookieTable上进行记录,转化的IntPtr就是它的地址,在GCHandle::Free的时候会从这个cook上删除。因为有个这个类级别的数据,GC才能通过它知道并忽略这些Object^
的回收。
private static GCHandleCookieTable s_cookieTable;
pin_ptr
应该采用了类似的技术,不过是在编译器级别进行了实现(从pin_ptr来看,它仅仅是定义了类型,具体的实现都是隐藏的,在编译器级别进行了行为的定义):�
template<typename T>
class pin_ptr
{
T* operator*();
};
在你的工具箱里,GCHandle在混合编程领域,应该是个利器。
网友评论