5

我需要使用来自 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* 时,它会设法做到这一点吗?这段代码安全吗?

谢谢。

4

2 回答 2

2

这就是我最终做的事情,而且效果很好。gcroot 的目的是在本机堆上存储托管引用,这正是我在这里所做的。

于 2013-04-04T02:35:49.597 回答
0

不!恰恰相反。gcroot 是一个原生类模板。您可以使用它以使用 clr 支持编译的本机类型存储托管内存的句柄。您通常会使用它将对本机对象的成员函数的调用转移到存储在 gcroot 类型成员中的托管对象。

编辑:我昨天在移动设备上输入代码示例有点尴尬......的预期和典型用法gcroot<T^>是在这些方面的某个地方:

// ICallback.h
struct ICallback {
    virtual void Invoke() = 0;
    virtual void Release() = 0;
    protected:
        ~ICallback() {}
};

这就是您的本机应用程序或库所看到和包含的内容。然后,您有一个使用 CLR 支持编译的混合组件,该组件在 a 中实现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^>...

于 2013-03-08T21:31:21.247 回答