我需要使用C ++ / CLI中的非托管API。此API存储指向任意用户数据和一些回调的void指针。然后它最终调用那些回调,将用户数据作为void *。
传递到目前为止,我有一个本机类将其“this”指针作为用户数据传递,并使用该指针将API调用回此类,即:
static void __stdcall Callback(void* userData) {
((MyType*)userData)->Method();
}
class MyType {
public:
MyType() { RegisterWithApi((void*)this, Callback); }
void Method();
};
我正在尝试使用托管类来翻译它。我发现gcroot类型可以用来在本机代码中安全地存储托管引用,所以我现在就是这样做的:
// This is called by the native API
static void __stdcall Callback(void* userData) {
// Cast back to gcroot and call into managed code
(*(gcroot<MyType^>*)userData)->Method();
}
ref class MyType {
gcroot<MyType^>* m_self;
public:
MyType() {
m_self = new gcroot<MyType^>;
RegisterWithApi((void*)m_self, Callback);
}
~MyType() { delete m_self; }
// Method we want called by the native API
void Method();
}
虽然这对C ++ / CLI编译器来说似乎很好,但我并不完全放心。根据我的理解,gcroot以某种方式跟踪它在GC移动时的托管引用。它是否能够通过非托管代码存储为void *来实现此目的?这段代码安全吗?
感谢。
答案 0 :(得分:2)
这就是我最终做的事情,它完美无缺。 gcroot的目的是在本机堆上存储托管引用,这正是我在这里所做的。
答案 1 :(得分:0)
没有!这恰恰相反。 gcroot是一个本机类模板。您可以使用它来存储使用clr支持编译的本机类型的托管内存的句柄。您通常会使用它将对本机对象的成员函数的调用转移到存储在gcroot类型成员中的托管对象。
编辑:昨天我在移动设备上打字代码示例有点尴尬...... gcroot<T^>
的预期和典型用法是这样的:
// ICallback.h
struct ICallback {
virtual void Invoke() = 0;
virtual void Release() = 0;
protected:
~ICallback() {}
};
这是您的原生应用或图书馆看到并包含的内容。然后,您有一个使用CLR支持编译的混合组件,它实现ICallback
并将句柄存储到gcroot<ManagedType^>
中的某个托管对象:
// Callback.cpp (this translation unit must be compiled with /clr)
// I did not compile and test, but you get the point...
template<class T^> class Callback : public ICallback {
gcroot<T^> m_Managed;
virtual void Invoke()
{
m_Managed->Invoke();
}
virtual void Release()
{
delete this;
}
public:
Callback(T^ p_Managed) : m_Managed(p_Managed) {}
};
__declspec( dllexport ) ICallback* CreateCallback()
{
auto t_Managed = gcnew SomeManagedType();
return new Callback<System::Action^>(
gcnew System::Action(t_Managed, &SomeManagedType::Method));
}
您的原生应用调用CreateCallback
,接收ICallback
的实例Invoke
- d调用托管类型的方法,保存在gcroot<System::Action^>
...