Search code examples
c#disposecomobject

Releasing Multiple References to COM Objects Separately


I'm not super handy at managing COM Objects. I've done a fair bit of reading, but I can't quite wrap my head around what happens when you have two references pointed at a single COM Object, and you release one of them. Consider the following [semi-]hypothetical:

I've created a basic wrapper class for Excel Workbooks in C#:

using ExcelInterop = Microsoft.Office.Interop.Excel;
...

public class WorkbookWrapper : IDisposable
{
    protected internal ExcelInterop.Workbook _wb;

    public WorkbookWrapper(ExcelInterop.Workbook workbook)
    {
        _wb = workbook;
    }

    public WorkbookWrapper(WorkbookWrapper workbookWrapper)
    {
        _wb = workbookWrapper._wb;
    }

    #region IDisposable

    // basic Dispose function which releases _wb COM Object (omitted to shorten post length)

    #endregion

    ...
}

For one particular project I'm working on, I've found it necessary to attach some additional information to WorkbookWrapper objects, so I've extended it:

class LinkingWorkbookWrapper : WorkbookWrapper
{
    internal string workbookGUID = null;

    internal LinkingWorkbookWrapper(WorkbookWrapper workbookWrapper, string GUID) : base(workbook)
    {
        workbookGUID = GUID;
    }

    ...
}

This particular class uses the WorkbookWrapper(WorkbookWrapper) constructor to store a new reference to the Interop Workbook, this is suddenly problematic when using functions like the following:

public static void ProcessAllCharts(WorkbookWrapper wbw)
{
    LinkingWorkbookWrapper lwbw = null;
    if(wbw is LinkingWorkbookWrapper)
        lwbw = (LinkingWorkbookWrapper)wbw;
    else
        lwbw = new LinkingWorkbookWrapper(wbw, GenerateGUID());

    //... do stuff ...
}

For now let's say that the function must accept normal WorkbookWrappers. When is the appropriate time to dispose of lwbw? If I do so at the end of ProcessAllCharts I may separate the COM Object referenced in the original wbw from its RCW; on the other hand, if I do not dispose of lwbw, I run the risk of leaving a reference to the COM Object floating in the ether.


Solution

  • This one really belongs to Hans Passant who casually dropped by the comments of the original question but never posted an actual answer.

    In a lengthy answer to another question, he details why manually releasing is a fool's errand at best. He also goes into a fair bit of detail as to why we actually get these ghost instances hanging around, and how to rid ourselves of them. I really encourage anyone having this problem to go read the entire post (it's very informative) but if you're in a pinch and need to now, let me summarize it:

    enter image description here

    Yup, that's it. That's all there is to it. The debugger is a bit of a nag and seems to like convincing the garbage collector that some things are still important when they're really not. I know, you don't think this is your problem, you think this is craziness. I felt the same way, but give it a try; fixed my problem in a jiffy.

    Thanks again, Hans Passant, you stoic C-Sharpian you.