.NET allows programmers to reuse COM components in their managed code. To make this possible, a managed wrapper object around the native object is needed. Besides that, one can use the COM object like any other managed object. Even if it sounds simple, you have to be aware of the differences between the CLR's object lifetime management and the COM version of object lifetime management.
COM programmers have to call Release on every interface that has been AddRef'ed. For C# programmers using COM objects, that means AddRef
is called when:
- a COM object is created
- a COM object is returned by calling a method or a property
- a COM object is cast'ed to another COM interface type
To release a COM object in C#, there are two options:
- Leave the GC to collect managed wrappers and to call their finalizers that will call Release on native COM object
- Manually call
Marshal.ReleaseComObject
on every interface used in the code
Let's see a short example using COM objects exposed by Internet Explorer. The code below changes the color of every link in an HTML document.
foreach (IHTMLElement elem in doc.all)
{
IHTMLAnchorElement anchor = elem as IHTMLAnchorElement;
if (anchor != null)
{
elem.style.color = "red";
}
}
This first approach leaves the task of releasing COM objects to garbage collector. Let's manually release COM objects now:
IHTMLElementCollection allCollection = doc.all;
foreach (IHTMLElement crntElem in allCollection)
{
IHTMLAnchorElement anchor = crntElem as IHTMLAnchorElement;
if (anchor != null)
{
IHTMLStyle style = crntElem.style;
style.color = "red";
Marshal.ReleaseComObject(style);
Marshal.ReleaseComObject(anchor);
}
Marshal.ReleaseComObject(crntElem);
}
Marshal.ReleaseComObject(allCollection);
As you can see, the number of code lines doubles! I personally prefer to leave the task of releasing COM objects to GC even if they will be eventually released after some time when GC comes into action.
Some might be tempted to call GC.Collect
after a large chunk of code that works with COM objects but this could be even worse because other managed objects could be promoted to next GC generation and their lifespan is therefore longer than necessary.
In theory, it is possible to create a lot of large COM objects that will exceed the native heap while the managed heap has a lot of available memory because managed wrappers are smaller in size. GC won't be called in this scenario so the native heap won't be freed.
If your application suffers from this kind of memory allocation problem, maybe using COM objects from managed code is not the best approach for you.
CodeProject