Hello there,
I have some doubts about the best practice for using COM automation,
the Runtime
Callable Wrapper (RCW) and Marshal.ReleaseComObject.
So the question is/are:
Do I need to release each COM object explicit by a call to
Marshal.ReleaseComObject, does the RCW take care of that or does it
leaks
unmanaged resources?
If I understand the docs correctly without the explicit call to
Marshal.ReleaseComObject the RCW objects release the COM objects in
their
finalizer. But as I read several times in this NG, it's a bad thing to
depend
on the finalizer to release unmanaged resources because the garbage
collector
runs non-deterministic so someone never knows when.
Unfortunately the RCW objects don't implement the dispose pattern so
someone
could dispose the underlying COM object deterministic. And even then,
the
codingstyle is a mess (as you'll see in my examples later).
So what is the best (or common) practice for COM automation:
Example 1: Explicit calls of Marshal.ReleaseComObject
==========================
Excel.ApplicationClass excelApp = null;
Excel.Workbooks workbooks = null;
Excel.Workbook wb = null;
Excel.Worksheet ws = null;
try
{
excelApp = new Excel.ApplicationClass();
excelApp.DisplayAlerts = false;
excelApp.EnableEvents = false;
// enumerate all available worsheets
workbooks = excelApp.Workbooks;
wb = workbooks.Open(path, 0, true, 5, "", "", true,
Excel.XlPlatform.xlWindows,
"", false, false, 0, false, false,
Excel.XlCorruptLoad.xlNormalLoad);
foreach (ws in wb.Sheets)
{
Console.WriteLine("Sheet: {0}\n", ws.Name);
Marshal.ReleaseComObject(ws); // don't even know if thats allowed
here within the iterator loop
}
}
finally
{
if (ws != null) Marshal.ReleaseComObject(ws);
if (wb != null) Marshal.ReleaseComObject(wb);
if (workbooks != null) Marshal.ReleaseComObject(workbooks);
if (excelApp != null)
{
excelApp.Quit();
Marshal.ReleaseComObject(excelApp);
}
}
Example 2: Using a wrapper to implement dispose pattern
========================
public class ComObj<T: IDisposable where T : class
{
private T _obj;
public ComObj() { _obj = null; }
public ComObj(T x) { _obj = x; }
~ComObj() { Dispose(false); }
public T Obj {
get { return _obj; }
set {
if (_obj != null) Marshal.ReleaseComObject(_obj);
_obj = value;
}
}
protected virtual void Dispose(bool disposing)
{
// clean up unmanaged resources
if (_obj != null) Marshal.ReleaseComObject(_obj);
}
void IDisposable.Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
using (ComObj<Excel.ApplicationClassexcelApp = new
ComObj<Excel.ApplicationClass>(new Excel.ApplicationClass()))
{
excelApp.Obj.DisplayAlerts = false;
excelApp.Obj.EnableEvents = false;
// enumerate all available worsheets
using (ComObj<Excel.Workbooksworkbooks = new
ComObj<Excel.Workbooks>(excelApp.Obj.Workbooks))
using (ComObj<Excel.Workbookwb = new
ComObj<Excel.Workbook>(workbooks.Obj.Open(path, 0, true, 5, "", "",
true, Excel.XlPlatform.xlWindows, "", false, false, 0,
false, false,
Excel.XlCorruptLoad.xlNormalLoad)))
foreach (Excel.Worksheet x in wb.Sheets)
using (ComObj<Excel.Worksheetws = new
ComObj<Excel.Worksheet>(x))
Console.WriteLine("Sheet: {0}\n", ws.Obj.Name);
excelApp.Obj.Quit();
}
Example 3: Let the finalizer of RCW free the COM objects
=======================
Excel.ApplicationClass excelApp = new Excel.ApplicationClass();
excelApp.DisplayAlerts = false;
excelApp.EnableEvents = false;
// enumerate all available worsheets
Excel.Workbook wb = excelApp.Workbooks.Open(path, 0, true, 5, "", "",
true, Excel.XlPlatform.xlWindows,
"", false, false, 0, false, false,
Excel.XlCorruptLoad.xlNormalLoad);
foreach (Excel.Worksheet ws in wb.Sheets)
Console.WriteLine("Sheet: {0}\n", ws.Name);
excelApp.Quit();
Example 3 is the most short and readable code but I'm not sure if I
should depend on the finalizer to
clean up the unmanaged (COM) resources.
Example 1 and 2 seems the most relieable mechanism but had some big
disadvantages:
- each COM object has to be wrapped in a variable (Ex1) or wrapper
object (Ex2)
- the foreach loop is a pain in the butt
- unused return values (COM objects) had to be freed explicitly
- property chaining (aaa.bbb.ccc.ddd) is not possible, otherwise the
intermediate objects would not be released.
So what is the best/usual practice to implement COM automation?
Regards,
Martin