I had an interview the other day and thought I would write abut one of the questions I had. It involved releasing references to COM objects in .NET.
The pattern to do this is unsurprisingly called the Dispose pattern. The problems with using COM components in managed code are as follows:
1. Developers can accidentally forget to call the dispose() method on your class.
2. Developer might accidentally call the dispose() method twice
3. The dispose method needs to be able to release the COM reference and free up the memory it used
To solve this problem, we need our class to implement the IDisposable Interface. We can then implement the Dispose() method to clean up resources.
public class ClassWithReferenceToCom : IDisposable
{
private ComObject _comObject;
public void Dispose()
{
}
}
We now need to write the code to dispose the object. To release COM objects the method is Marshal.ReleaseComObject(_comObject);
This method decreases the list of references COM objects by one. When this happens, the object is dereferenced. The problem with this, is that if it is called twice we will remove TWO objects. We will accidentally remove some other COM object we might be referencing.
So to stop this happening we can add a boolean around the method so it is called only once.
public class ClassWithReferenceToCom : IDisposable
{
private ComObject _comObject;
private bool _isDisposing;
public void Dispose()
{
if (!_isDisposing)
{
Marshal.ReleaseComObject(_comObject);
_isDisposing = true;
}
}
}
Now it doesn’t matter if the Dispose() method is called twice.
But what if it isnt called at all?
We can add a destructor to ensure that when our C# object goes out of scope it will automatically call the Dispose() method.
public class ClassWithReferenceToCom : IDisposable
{
private ComObject _comObject;
private bool _isDisposing;
public void Dispose()
{
if (!_isDisposing)
{
Marshal.ReleaseComObject(_comObject);
_isDisposing = true;
}
}
public ~ClassWithReferenceToCom()
{
Dispose();
}
}
All good so far. But there is one more thing to consider. When our object is destroyed by the garbage collector, by calling disposed, C# will also call the destructor, which will then call the Dispose() method again. We have an infinite loop situation.
We can solve this with the GC static method GC.SuppressFinalize(this);
We now have the full dispose pattern.
public class ClassWithReferenceToCom : IDisposable
{
private ComObject _comObject;
private bool _isDisposing;
public void Dispose()
{
if (!_isDisposing)
{
Marshal.ReleaseComObject(_comObject);
GC.SuppressFinalize(this);
_isDisposing = true;
}
}
public ~ClassWithReferenceToCom()
{
Dispose();
}
}
Now we can be sure our COM object is safely removed from memory when our C# object is destroyed.
[...] exist if the developer who uses the library forgets to call dispose. You can address this with the Dispose pattern. (Just substitute the release of the COM object with the code to release the event handlers in this [...]