4

我在 c 中有一些函数,我会在 .net 应用程序中使用它。为此,我用 C++/cli 编写了一个 Wrapper 类。

在 c 接口中有一个回调函数,并将其包装在 .net 委托中。

但是我应该如何释放回调 gcHandle 的非托管资源?是否允许从终结器中的 GCHandle 调用 IsAllocated 和 Free?因为它是一个托管资源,gc 是否已经发布了它?

这是c接口的代码:

// C functions

#ifdef __cplusplus
extern "C" {
#endif

    typedef void (*my_native_callback)(const uint8_t buffer[], uint32_t buffer_len);

    void register_callback(my_native_callback c, uint32_t* id);

    void remove_callback(uint32_t id);

#ifdef __cplusplus
}
#endif

这里是 .net 包装器:

// .net wrapper (c++/cli)
public ref class MyWrapper
{
public:
    MyWrapper()
    {
        RegisterCallback();
    }

    // Destructor.
    ~MyWrapper()
    {
        this->!MyWrapper();
    }

protected:
    // Finalizer.
    !MyWrapper()
    {
        RemoveCallback();       // <- Is this safe?
        // ... release other unmanaged ressorces
    }

private:
    void RegisterCallback()
    {
        uint32_t id = 0;
        callbackDelegate_ = gcnew MyCallbackDelegate(this, &MyWrapper::OnCallback);
        callbackHandle_ = System::Runtime::InteropServices::GCHandle::Alloc(callbackDelegate_);
        System::IntPtr delegatePointer = System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(callbackDelegate_);
        register_callback(static_cast<my_native_callback>(delegatePointer.ToPointer()), &id);
        callbackId_ = id;
    }

    void RemoveCallback()
    {
        if (callbackId_)
        {
            remove_callback(callbackId_);
            callbackId_ = 0;
        }
        if (callbackHandle_.IsAllocated)        // It this safe in the finalizer?
        {
            callbackHandle_.Free();             // It this safe in the finalizer?
        }
        callbackDelegate_ = nullptr;            // It this safe in the finalizer?
    }


    void OnCallback(const uint8_t* buffer, uint32_t buffer_len)
    {
        // ...
    }

private:
    [System::Runtime::InteropServices::UnmanagedFunctionPointer(System::Runtime::InteropServices::CallingConvention::Cdecl)]
    delegate void MyCallbackDelegate(const uint8_t* buffer, uint32_t buffer_len);   
    MyCallbackDelegate^ callbackDelegate_;
    System::Runtime::InteropServices::GCHandle callbackHandle_;
    int callbackId_;
    // ... 
};

代码片段是否安全,最佳实践是什么?

先感谢您。

4

1 回答 1

6

无需向委托对象添加额外的 GCHandle 引用。您已经在 callbackDelegate_ 字段中正确存储了一个引用,足以让垃圾收集器相信委托正在使用中并且不应该被收集。不需要额外的参考。

只需从您的代码中删除 callbackHandle_ 即可。

于 2013-03-11T14:05:39.550 回答