Lately I was doing a little project that involved a DLL module (that was created with C#) and that I needed to use in my application (that was written in unmanaged C++) For this to work I was using ATL/COM.
I noticed that even though I’m using a _com_ptr_t in my C++ application for handling my core COM interface, C# object’s destructor is called only when my application is closed.
Let me give you some source to make things a bit more clearer:
Some of my C# code:
[ComVisible(true)]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)]
public interface ITestCOM
{
[DispId(1)]
void Connect([In, MarshalAs(UnmanagedType.U2)] ushort value);
}
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[ComSourceInterfaces(typeof(ITestCOMEvents))]
public partial class TestCOM : ITestCOM
{
...
~TestCOM()
{
MessageBox.Show("DESTRUCTOR");
}
...
public void Connect(ushort value)
{
...
}
}
I’m creating the .tlb file using something like this:
“RegAsm.exe TestCOM.dll /tlb:Test.tlb /codebase“
In my C++ header I have:
#import "C:\...\mscorlib.tlb"
#import "......\TestCOM.tlb" named_guids exclude("ISupportErrorInfo")
#include <afxdisp.h>
#include <atlcom.h>
class Unit : public ::IDispEventSimpleImpl<0, Unit, &__uuidof(TestCOM::ITestCOMEvents)>
{
public:
BEGIN_SINK_MAP(Unit)
SINK_ENTRY_INFO(0, __uuidof(TestCOM::ITestCOMEvents), 0x1, OnEventCallback, &OnEventCallbackDef)
END_SINK_MAP()
...
private
TestCOM::ITestCOMPtr mTestCOM;
// NOTE: This would be the same as "_com_ptr_t<_com_IIID<TestCOM::ITestCOM, &__uuidof(TestCOM::ITestCOM)> > mTestCOM;"
}
And my C++ source file I create my “mTestCOM” like this:
mTestCOM.CreateInstance(TestCOM::CLSID_TestCOM)
And basically that’s it.. I can use any of my C# “TestCOM” object’s functions like this:
mTestCOM->Connect(7);
The question is:
Why my C# TestCOM object’s destructor is called only when my application is closed, and not when my C++ “Unit” object is destroyed?
Though I’m not familiar with C# and COM integration, I do know that destructors in C# work very differently to destructors in C++. A C# object is memory-managed and garbage-collected. This means that at some point after the object stops being referenced by the application it belongs to and becomes ‘unreachable’, the garbage collector will destroy it.
So the first important thing is that the delay between an object being abandoned and the garbage collector destroying it is nondeterministic… it will happen “at some point in the future”, which may well be at the point the application terminates.
Secondly, the garbage collector is not always running. There is the concept of “memory pressure”, when your application is allocating large chunks of memory and the free memory available to it is running out… at that point, the garbage collector will fire to get rid of old, unreachable objects. If your application does not allocate lots of memory it will not suffer from any memory pressure and the garbage collector will not need to run.
If you want to deterministically clean up some of a managed object’s resources, you will need to use something like the
IDisposableinterface and call theDisposemethod explicitly.