3

The following code sample worked just fine in Excel 2007, but when I installed Excel 2010 (32bit) it would leave the excel.exe process open unless I added the GC.Collect(). My simple question is am I doing something wrong? It looks like to me like I am releasing everything I use.

    public override void Update()
    {

        StatusBox.AddStatus("Opening File " + ImportPath);

        Microsoft.Office.Interop.Excel.Application app = new Microsoft.Office.Interop.Excel.Application();
        Microsoft.Office.Interop.Excel.Workbook wb = app.Workbooks.Open(ImportPath, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);
        Microsoft.Office.Interop.Excel.Worksheet ws = (Microsoft.Office.Interop.Excel.Worksheet)wb.Sheets[1];

        Range rng = ws.Cells.SpecialCells(XlCellType.xlCellTypeLastCell, Type.Missing);

        int LastRow = rng.Row;

        StatusBox.AddStatus(LastRow.ToString() + " Rows Found in File");


        StatusBox.AddStatus("Closing File " + ImportPath);

        System.Runtime.InteropServices.Marshal.ReleaseComObject(rng);
        rng = null;

        System.Runtime.InteropServices.Marshal.ReleaseComObject(ws);
        ws = null;

        wb.Close(true, ImportPath, null);
        System.Runtime.InteropServices.Marshal.ReleaseComObject(wb);
        wb = null;

        GC.Collect();

        app.Quit();
        System.Runtime.InteropServices.Marshal.ReleaseComObject(app);
        app = null;
    }
Matthew Bierman
  • 360
  • 3
  • 12
  • I do quite a bit of COM interop with Excel and MapPoint. I don't think I've ever needed to use ReleaseComObject. Clearing all null references and calling the relevant close/shutdown methods has been enough. Can anyone confirm / elaborate on the possible need for ReleaseComObject() in these Office type situations? – winwaed Oct 27 '10 at 15:45
  • I added ReleaseComObject when I was having trouble with hanging excel references the first time I wrote the code for Excel 2003. I have actually read that the var = null; might be unnecessary. – Matthew Bierman Oct 27 '10 at 20:30

1 Answers1

1

You need to call both GC.Collect / GC.WaitForPendingFinalizers and Marshall.FinalReleaseComObject.

See my answer here for details:

How do I properly clean up Excel interop objects?

Note that the advice (and apparently the more popular answer) to "never use two dots" in any given command is valid, but virtually impossible to enforce in practice. If you make any mistake anywhere in your code, the Excel application will hang and there is no profiling tool on the planet that can help you -- you'd have to review all your code by eye. For a large code base, this is essentially impossible.

In your code, you do not have a call to GC.WaitForPendingFinalizers after your call to GC.Collect. This is necessary to ensure that your garbage collection calls are synchronous. (GC.Collect operates on a different thread, if you don't wait for it, the collection could occur out of order with respect to your subseqent object releases and you want to release minor COM objects, like Ranges, first, and the major COM objects like Workbooks and the Application, last.) After calling GC.Collect and GC.WaitForPendingFinalizers, you would then want to call Marshall.FinalReleaseComObject on your named references.

So, in short, the strategy is to call GC.Collect and GC.WaitForPendingFinalizers to release the COM objects to which you do not hold a reference and call Marshall.FinalReleaseComObject to release the COM objects to which you do hold a named reference.

-- Mike

Community
  • 1
  • 1
Mike Rosenblum
  • 12,027
  • 6
  • 48
  • 64
  • While Hans listed correctly if was my reference to "ws.Cells." that was the problem (although I am unsure why it was not a problem in Excel 2007) I am marking this as the answer because it is a better explanation. There are also situations where I might pass my excel references to other libraries, while those are also under my control, theoretically they might not be. – Matthew Bierman Oct 28 '10 at 13:31
  • Glad that WS.Cells did the trick, but, yes, it is odd that different versions of Excel behave differently here. It could even be that memory pressure is different in each version so that GC.Collect is being implicitly called at the right time more or less by luck in one version but not the other. Your discussion of third party libraries, though, is the real problem -- some of them simply do not release their references properly, whether your code interacts with them or not. In these cases you have no choice but to use Process.Kill after calling Application.Close (or don't use the add-ins). – Mike Rosenblum Oct 28 '10 at 22:06