I have some functions in c and I would use this in a .net application. For this I wrote an Wrapper class with C++/cli.
In the c interface is a callback function and wrapped this in a .net delegate.
But how should I release the unmanaged ressources for the callback gcHandle? Is it allowd to call IsAllocated and Free from a GCHandle in the finalizer? Because it is an managed ressource and is it possibile that the gc already release it?
Here is the code for the c interface:
// 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
And here the .net wrapper:
// .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_;
// ...
};
Is the code snippet safe and what is best practice for this?
Thank you in advance.
There is no need to add an extra GCHandle reference to the delegate object. You are already correctly store a reference in the callbackDelegate_ field, enough to convince the garbage collector that the delegate is in use and should not be collected. No additional reference is required.
Just remove callbackHandle_ from your code.