这种gcroot的使用安全吗?

时间:2013-03-08 21:07:59

标签: c++ .net garbage-collection interop c++-cli

我需要使用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 *来实现此目的?这段代码安全吗?

感谢。

2 个答案:

答案 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^> ...