如何在不丢失引用的情况下通过非托管代码传递引用对象(CLR)?

时间:2014-08-22 12:02:51

标签: .net c++-cli interop

我是一个非托管第三方库的包装器。该库允许注册回调。我使用委托使用了回调,但问题是,在注册回调时,我可以提供额外的用户数据void*,这些数据不会被库触及,而是作为参数传递给回调。

在代码中:

//interface
BOOL register_callback(LPCALLBACKFN pfnCallback, void* lpvUserData);

C ++ - CLI侧:

bool LibWrapper::RegisterCallback()
{
    resetLastError();

    CallbackDelegate^ callback = gcnew CallbackDelegate(
        this,
        &LibWrapper::test_callback);

    GCHandle callbackHandle = GCHandle::Alloc(callback);
    IntPtr ptrCallback = Marshal::GetFunctionPointerForDelegate(callback);
    LPCALLBACKFN nativeCallbackPtr = \
            static_cast<LPCALLBACKFN>(ptrCallback.ToPointer());

    try
    {
        return register_callback(nativeCallbackPtr,/*void* data*/ nullptr);//!!!
    }
    catch(...)
    {
        setLastError("Unknown error when trying to register a callback.");
        return false;
    }
}

我有什么方法可以传递Object^而不只是nullptr作为用户数据? 对象本身可以是从简单的托管结构到显然引用其他节点的树节点的任何东西,因此我想找到一种不涉及创建整个对象的深度非托管副本的方法。

如果不可能,我也可以使用解决方法并保存我自己的请求回调列表,并自己附加相应的userData-Object。在那种情况下,它永远不会离开管理世界,但它仍然感觉像作弊。

1 个答案:

答案 0 :(得分:3)

好吧,你可以再次使用GCHandle :: Alloc(),使用GCHandle :: ToIntPtr()获取原始指针,并使用GCHandle :: FromIntPtr()在回调中再次恢复对象引用。

但这并不是必需的。委托对函数指针的强大之处在于它捕获对象引用。 CallbackDelegate构造函数调用中的this参数。这确保了您的test_callback()方法可以是实例函数并访问对象成员。即使从本地代码调用它,它也知道有关托管对象的bean。当您调用Marshal :: GetFunctionPointerForDelegate()时创建的存根使用有效的 this 引用调用它。因此,不需要使用void *参数,只需将对象改为类的字段即可。

你应该担心清理btw,当本机代码不能再进行回调时,一定要调用GCHandle :: Free()。不这样做会永远泄漏托管对象。