In some C++/CLI code I have a native class which has a factory method GetWrapper() for creating its own managed .NET wrapper object. Internally, it holds a weak reference to its wrapper via GCHandle. When GetWrapper() is called, the GCHandle is checked and either a handle to the existing wrapper is returned, or (if it does not point to an object anymore, because the old wrapper object has been destroyed by the Garbage Collector) a new one is created an returned.
// .h
class NativeClass
{
public:
WrapperClass^ GetWrapper();
private:
WrapperClass^ GetNewWrapper();
GCHandle m_wrapperGCHandle;
};
// .cpp
WrapperClass^ NativeClass::GetWrapper()
{
if(m_wrapperGCHandle.IsAllocated)
{
try
{
WrapperClass^ wrapper = nullptr;
wrapper = dynamic_cast<WrapperClass^>(wrapperGCHandle.Target);
if(wrapper == nullptr)
{
return GetNewWrapper();
}
else
{
return wrapper;
}
}
catch(System::InvalidOperationException^)
{
return GetNewWrapper();
}
else
{
return GetNewWrapper();
}
}
WrapperClass^ NativeClass::GetNewWrapper()
{
WrapperClass^ wrapper = gcnew WrapperClass(/*some args*/);
m_wrapperGCHandle = GCHandle::Alloc(wrapper, GCHandleType::Weak);
}
The strange thing now is that m_wrapperGCHandle.IsAllocated always returns true, even if the wrapper has been garbage-collected. The MSDN tells to “Use this property when using Weak handles to determine if the GCHandle is still available.”. But it’s always true. If it’s not available then the Target is a nullptr instead.
Am I missing something or is the MSDN wrong?
My read of the MSDN doco is that
m_wrapprGCHandle.IsAllocatedwill return true untilm_wrapperGCHandle.Freeis called – theIsAllocatedproperty is checking for the state of the handle rather than the state of the reference held by the handle.As you have noted,
m_wrapperGCHandle.Targetis null when the object has been garbage collected. I have used a similar method to what you have in your example code to generate managed wrapper classes, and I always check to see ifTargetis null and regenerate the wrapper object ifTargetis null.Also, a suggestion … it looks to me as if you have a handle leak in your code because you are calling
GCHandle::Allocwithout a correspondingm_wrapperGCHandle.Freecall. Try putting the call toAllocin the class constructor and the call toFreein the destructor:then your
GetNewWrappermethod is simply:and you can remove the
if(m_wrapperGCHandle.IsAllocated) - elsechain from theGetWrappermethod.