Possible Duplicate:
How to properly clean up Excel interop objects in C#
I’ve read many of the other threads here about managing COM references while using the .Net-Excel interop to make sure the Excel process exits correctly upon exit, and so far the techniques have been working very well, but I recently came across a problem when adding new worksheets to an existing workbook file.
The code below leaves a zombie Excel process.
If I add a worksheet to a newly created workbook file, it exits fine. If I run the code excluding the .Add() line, it exits fine. (The existing file I’m reading from is an empty file created by the commented out code)
Any ideas?
//using Excel = Microsoft.Office.Interop.Excel; //using System.Runtime.InteropServices; public static void AddTest() { string filename = @'C:\addtest.xls'; object m = Type.Missing; Excel.Application excelapp = new Excel.Application(); if (excelapp == null) throw new Exception('Can't start Excel'); Excel.Workbooks wbs = excelapp.Workbooks; //if I create a new file and then add a worksheet, //it will exit normally (i.e. if you uncomment the next two lines //and comment out the .Open() line below): //Excel.Workbook wb = wbs.Add(Excel.XlWBATemplate.xlWBATWorksheet); //wb.SaveAs(filename, m, m, m, m, m, // Excel.XlSaveAsAccessMode.xlExclusive, // m, m, m, m, m); //but if I open an existing file and add a worksheet, //it won't exit (leaves zombie excel processes) Excel.Workbook wb = wbs.Open(filename, m, m, m, m, m, m, Excel.XlPlatform.xlWindows, m, m, m, m, m, m, m); Excel.Sheets sheets = wb.Worksheets; //This is the offending line: Excel.Worksheet wsnew = sheets.Add(m, m, m, m) as Excel.Worksheet; //N.B. it doesn't help if I try specifying the parameters in Add() above wb.Save(); wb.Close(m, m, m); //overkill to do GC so many times, but shows that doesn't fix it GC(); //cleanup COM references //changing these all to FinalReleaseComObject doesn't help either while (Marshal.ReleaseComObject(wsnew) > 0) { } wsnew = null; while (Marshal.ReleaseComObject(sheets) > 0) { } sheets = null; while (Marshal.ReleaseComObject(wb) > 0) { } wb = null; while (Marshal.ReleaseComObject(wbs) > 0) { } wbs = null; GC(); excelapp.Quit(); while (Marshal.ReleaseComObject(excelapp) > 0) { } excelapp = null; GC(); } public static void GC() { System.GC.Collect(); System.GC.WaitForPendingFinalizers(); System.GC.Collect(); System.GC.WaitForPendingFinalizers(); }
I don’t have the code to hand, but I did run into a similar problem. If I recall correctly, I ended up retrieving the process id of the excel instance, and killing it (after a suitable wait period, and when the other method failed).
I think I used:
GetWindowThreadProcessId(via P/Invoke) on the excel object hwnd property to get the process id, and then usedProcess.GetProcessByIdto get a process object. Once I’d done that, I’d callKillon the process.EDIT: I have to admit, this isn’t the ideal solution, but if you can’t find the rogue interface that isn’t being released, then this will fix it in true eggshell/sledgehammer fashion. 😉
EDIT2: You don’t have to call
Killon the process object immediately… You could first try callingClosebefore resorting toKill.