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 WorkbookWrapper
s. 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.
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:
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.