Sunday, November 25, 2007

RCW Refcounts

You're accessing Microsoft Excel from .NET managed code using the Primary Interop Assembly (PIA). Your program ends, but the Excel process is still alive in task manager (a bad thing). It can happen in anytime you use a Runtime Callable Wrapper (RCW), but the problem is most acute in Office PIAs because of the peculiar VBA APIs.

The VBA syntax encourages stringing properties along, something like this (in C#):

    using Microsoft.Office.Interop.Excel;
    ...
    Application app = new Application();
    Workbook workbook = app.Workbooks.Open(...);
    Range range = workbook.Worksheets[1].UsedRange;
    ...

The RCW increments the reference count for each wrapped object, so you have to release the reference. For example, I might call "Release(workbook)" where Release is given by:

    private static void Release(Object o)
    {
        try
        {
            Int32 i = 1;
            while (o != null && i > 0)
            {
                i = Marshal.ReleaseComObject(o);
            }
        }
        catch
        {}
    }

It is my belief that the refcount will never be more than 1 as adding another managed reference is only copying the pointer to the wrapper, which holds the real interop reference. So the Release() method can be simplified a bit.

The tricky part is, in the VBA style syntax you don't declare references for the intermediate steps. Now you might think there is no refcount if you don't have a reference, but you'd be wrong. The wrapper still needs to be created at each step to call the method of the next property in the chain, and that adds the refcount. So if you don't keep a reference to release later, you're hosed.

Meanwhile, any time you throw an exception you will miss your Release(), so put the Release() calls in 'finally' blocks. I am fairly paranoid, so I release things in exactly the same order I referenced them. If I have to do things between releases that might throw an exception, the try-finally blocks can nest pretty badly, but that's the way it goes.

No comments: