Newbie Question: COM automation / Marshal.ReleaseComObject / RCW /garbage collector

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • xrxst32

    Newbie Question: COM automation / Marshal.ReleaseComObject / RCW /garbage collector

    Hello there,
    I have some doubts about the best practice for using COM automation,
    the Runtime
    Callable Wrapper (RCW) and Marshal.Release ComObject.

    So the question is/are:
    Do I need to release each COM object explicit by a call to
    Marshal.Release ComObject, 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.Release ComObject 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.Release ComObject
    =============== ===========

    Excel.Applicati onClass excelApp = null;
    Excel.Workbooks workbooks = null;
    Excel.Workbook wb = null;
    Excel.Worksheet ws = null;
    try
    {
    excelApp = new Excel.Applicati onClass();
    excelApp.Displa yAlerts = false;
    excelApp.Enable Events = false;

    // enumerate all available worsheets
    workbooks = excelApp.Workbo oks;
    wb = workbooks.Open( path, 0, true, 5, "", "", true,
    Excel.XlPlatfor m.xlWindows,
    "", false, false, 0, false, false,
    Excel.XlCorrupt Load.xlNormalLo ad);
    foreach (ws in wb.Sheets)
    {
    Console.WriteLi ne("Sheet: {0}\n", ws.Name);
    Marshal.Release ComObject(ws); // don't even know if thats allowed
    here within the iterator loop
    }
    }
    finally
    {
    if (ws != null) Marshal.Release ComObject(ws);
    if (wb != null) Marshal.Release ComObject(wb);
    if (workbooks != null) Marshal.Release ComObject(workb ooks);
    if (excelApp != null)
    {
    excelApp.Quit() ;
    Marshal.Release ComObject(excel App);
    }
    }




    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.Release ComObject(_obj) ;
    _obj = value;
    }
    }
    protected virtual void Dispose(bool disposing)
    {
    // clean up unmanaged resources
    if (_obj != null) Marshal.Release ComObject(_obj) ;
    }
    void IDisposable.Dis pose()
    {
    Dispose(true);
    GC.SuppressFina lize(this);
    }
    }



    using (ComObj<Excel.A pplicationClass excelApp = new
    ComObj<Excel.Ap plicationClass> (new Excel.Applicati onClass()))
    {
    excelApp.Obj.Di splayAlerts = false;
    excelApp.Obj.En ableEvents = false;

    // enumerate all available worsheets
    using (ComObj<Excel.W orkbooksworkboo ks = new
    ComObj<Excel.Wo rkbooks>(excelA pp.Obj.Workbook s))
    using (ComObj<Excel.W orkbookwb = new
    ComObj<Excel.Wo rkbook>(workboo ks.Obj.Open(pat h, 0, true, 5, "", "",
    true, Excel.XlPlatfor m.xlWindows, "", false, false, 0,
    false, false,
    Excel.XlCorrupt Load.xlNormalLo ad)))
    foreach (Excel.Workshee t x in wb.Sheets)
    using (ComObj<Excel.W orksheetws = new
    ComObj<Excel.Wo rksheet>(x))
    Console.WriteLi ne("Sheet: {0}\n", ws.Obj.Name);
    excelApp.Obj.Qu it();
    }




    Example 3: Let the finalizer of RCW free the COM objects
    =============== ========

    Excel.Applicati onClass excelApp = new Excel.Applicati onClass();
    excelApp.Displa yAlerts = false;
    excelApp.Enable Events = false;

    // enumerate all available worsheets
    Excel.Workbook wb = excelApp.Workbo oks.Open(path, 0, true, 5, "", "",
    true, Excel.XlPlatfor m.xlWindows,
    "", false, false, 0, false, false,
    Excel.XlCorrupt Load.xlNormalLo ad);
    foreach (Excel.Workshee t ws in wb.Sheets)
    Console.WriteLi ne("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.dd d) is not possible, otherwise the
    intermediate objects would not be released.


    So what is the best/usual practice to implement COM automation?
    Regards,
    Martin


Working...