Unable to close Excel.exe after Interop process

I have a problem with Excel Interop.

Excel.exe does not close even if you re-deploy the instances.

Here is my code:

using xl = Microsoft.Office.Interop.Excel; xl.Application excel = new xl.Application(); excel.Visible = true; excel.ScreenUpdating = false; if (wordFile.Contains(".csv") || wordFile.Contains(".xls")) { //typeExcel become a string of the document name string typeExcel = wordFile.ToString(); xl.Workbook workbook = excel.Workbooks.Open(typeExcel, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing); object outputFileName = null; if (wordFile.Contains(".xls")) { outputFileName = wordFile.Replace(".xls", ".pdf"); } else if (wordFile.Contains(".csv")) { outputFileName = wordFile.Replace(".csv", ".pdf"); } workbook.ExportAsFixedFormat(XlFixedFormatType.xlTypePDF, outputFileName, XlFixedFormatQuality.xlQualityStandard, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing); object saveChanges = xl.XlSaveAction.xlDoNotSaveChanges; ((xl._Workbook)workbook).Close(saveChanges, oMissing, oMissing); Marshal.ReleaseComObject(workbook); workbook = null; } 

I saw that with Marshal.RealeaseComObject this should be work, but nothing. How can i fix this?

Thank.

+24
c # winforms excel-interop
Jun 28 '13 at 14:43
source share
10 answers

A simple rule: avoid using expressions with two dots, such as:

 var workbook = excel.Workbooks.Open(/*params*/) 

... because in this way you create RCW objects not only for the workbook , but also for the Workbooks , and you must free it too (which is impossible if the reference to the object is not supported).

So the correct way would be:

 var workbooks = excel.Workbooks; var workbook = workbooks.Open(/*params*/) //business logic here Marshal.ReleaseComObject(workbook); Marshal.ReleaseComObject(workbooks); Marshal.ReleaseComObject(excel); 
+58
Jun 28 '13 at 14:50
source share

Here is a snippet of code that I wrote because I had the same problem as yours. Basically, you need to close the book, exit the application, and then release ALL of your COM objects (not just the Excel application object). Finally, call the garbage collector for a good measure.

  /// <summary> /// Disposes the current <see cref="ExcelGraph" /> object and cleans up any resources. /// </summary> public void Dispose() { // Cleanup xWorkbook.Close(false); xApp.Quit(); // Manual disposal because of COM while (Marshal.ReleaseComObject(xApp) != 0) { } while (Marshal.ReleaseComObject(xWorkbook) != 0) { } while (Marshal.ReleaseComObject(xWorksheets) != 0) { } while (Marshal.ReleaseComObject(xWorksheet) != 0) { } while (Marshal.ReleaseComObject(xCharts) != 0) { } while (Marshal.ReleaseComObject(xMyChart) != 0) { } while (Marshal.ReleaseComObject(xGraph) != 0) { } while (Marshal.ReleaseComObject(xSeriesColl) != 0) { } while (Marshal.ReleaseComObject(xSeries) != 0) { } xApp = null; xWorkbook = null; xWorksheets = null; xWorksheet = null; xCharts = null; xMyChart = null; xGraph = null; xSeriesColl = null; xSeries = null; GC.Collect(); GC.WaitForPendingFinalizers(); } 
+15
Jun 28 '13 at 14:50
source share

Rules - Never Use One Point Again

- one point

 var range = ((Range)xlWorksheet.Cells[rowIndex, setColumn]); var hyperLinks = range.Hyperlinks; hyperLinks.Add(range, data); 

- Two or more points

  (Range)xlWorksheet.Cells[rowIndex, setColumn]).Hyperlinks.Add(range, data); 

- Example

  using Microsoft.Office.Interop.Excel; Application xls = null; Workbooks workBooks = null; Workbook workBook = null; Sheets sheets = null; Worksheet workSheet1 = null; Worksheet workSheet2 = null; workBooks = xls.Workbooks; workBook = workBooks.Open(workSpaceFile); sheets = workBook.Worksheets; workSheet1 = (Worksheet)sheets[1]; // removing from Memory if (xls != null) { foreach (Microsoft.Office.Interop.Excel.Worksheet sheet in sheets) { ReleaseObject(sheet); } ReleaseObject(sheets); workBook.Close(); ReleaseObject(workBook); ReleaseObject(workBooks); xls.Application.Quit(); // THIS IS WHAT IS CAUSES EXCEL TO CLOSE xls.Quit(); ReleaseObject(xls); sheets = null; workBook = null; workBooks = null; xls = null; GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); GC.WaitForPendingFinalizers(); } 
+9
Jan 09 '14 at 15:44
source share

In your code you have:

 excel.Workbooks.Open(...) 

excel.Workbooks creates a COM object. Then you call the Open function from this COM object. However, you do not release the COM object when you are done.

This is a common problem when working with COM objects. Basically, there should never be more than one dot in your expression, because you will need to clear the COM objects when you are done.

The topic is simply too large to fully explore the answer, but I think you will find Jake Ginnivan's article on this topic extremely useful: VSTO and COM Interop

If you are tired of all the calls to ReleaseComObject, you may find this question helpful:
How to properly clean Microsoft interaction object in C # editor, 2012

+7
Jun 28 '13 at 14:49
source share

It’s hard to get rid of all the links, since you need to guess if the calls are:

 var workbook = excel.Workbooks.Open("") 

Creates an instance of Workbooks that you are not referencing.

Even links like:

 targetRange.Columns.AutoFit() 

Creates an instance of .Columns() if you do not know and do not release it correctly.

I ended up writing a class containing a list of references to objects that could dispose of all objects in reverse order.

The class has a list of objects and Add() functions for everything that you reference when using the Excel interaction, which returns the object itself:

  public List<Object> _interopObjectList = new List<Object>(); public Excel.Application add(Excel.Application obj) { _interopObjectList.Add(obj); return obj; } public Excel.Range add(Excel.Range obj) { _interopObjectList.Add(obj); return obj; } public Excel.Workbook add(Excel.Workbook obj) { _interopObjectList.Add(obj); return obj; } public Excel.Worksheet add(Excel.Worksheet obj) { _interopObjectList.Add(obj); return obj; } public Excel.Worksheets add(Excel.Worksheets obj) { _interopObjectList.Add(obj); return obj; } public Excel.Sheets add(Excel.Sheets obj) { _interopObjectList.Add(obj); return obj; } public Excel.Workbooks add(Excel.Workbooks obj) { _interopObjectList.Add(obj); return obj; } 

Then, to unregister the objects, I used the following code:

  //Release all registered interop objects in reverse order public void unregister() { //Loop object list in reverse order and release Office object for (int i=_interopObjectList.Count-1; i>=0 ; i -= 1) { ReleaseComObject(_interopObjectList[i]); } //Clear object list _interopObjectList.Clear(); } /// <summary> /// Release a com interop object /// </summary> /// <param name="obj"></param> public static void ReleaseComObject(object obj) { if (obj != null && InteropServices.Marshal.IsComObject(obj)) try { InteropServices.Marshal.FinalReleaseComObject(obj); } catch { } finally { obj = null; } GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); GC.WaitForPendingFinalizers(); } 

Then the principle is to create a class and write the links as follows:

 //Create helper class xlsHandler xlObj = new xlsHandler(); .. //Sample - Capture reference to excel application Excel.Application _excelApp = xlObj.add(new Excel.Application()); .. //Sample - Call .Autofit() on a cell range and capture reference to .Columns() xlObj.add(_targetCell.Columns).AutoFit(); .. //Release all objects collected by helper class xlObj.unregister(); 

Unbelievable code of great beauty, but can inspire something useful.

+6
Nov 27 '14 at 18:02
source share

As pointed out in other answers, using two points will create hidden links that cannot be closed by Marshal.FinalReleaseComObject . I just wanted to share my solution, which eliminates the need to remember Marshal.FinalReleaseComObject - it's really easy to miss, and the pain is to find the culprit.

I use the generic IDisposable wrapper class , which can be used for any COM object. It works like a charm, and it keeps everything beautiful and clean. I can even reuse private fields (e.g. this.worksheet ). It also automatically frees an object when something throws an error due to the nature of IDisposable (the Dispose method works like finally ).

 using Microsoft.Office.Interop.Excel; public class ExcelService { private _Worksheet worksheet; private class ComObject<TType> : IDisposable { public TType Instance { get; set; } public ComObject(TType instance) { this.Instance = instance; } public void Dispose() { System.Runtime.InteropServices.Marshal.FinalReleaseComObject(this.Instance); } } public void CreateExcelFile(string fullFilePath) { using (var comApplication = new ComObject<Application>(new Application())) { var excelInstance = comApplication.Instance; excelInstance.Visible = false; excelInstance.DisplayAlerts = false; try { using (var workbooks = new ComObject<Workbooks>(excelInstance.Workbooks)) using (var workbook = new ComObject<_Workbook>(workbooks.Instance.Add())) using (var comSheets = new ComObject<Sheets>(workbook.Instance.Sheets)) { using (var comSheet = new ComObject<_Worksheet>(comSheets.Instance["Sheet1"])) { this.worksheet = comSheet.Instance; this.worksheet.Name = "Action"; this.worksheet.Visible = XlSheetVisibility.xlSheetHidden; } using (var comSheet = new ComObject<_Worksheet>(comSheets.Instance["Sheet2"])) { this.worksheet = comSheet.Instance; this.worksheet.Name = "Status"; this.worksheet.Visible = XlSheetVisibility.xlSheetHidden; } using (var comSheet = new ComObject<_Worksheet>(comSheets.Instance["Sheet3"])) { this.worksheet = comSheet.Instance; this.worksheet.Name = "ItemPrices"; this.worksheet.Activate(); using (var comRange = new ComObject<Range>(this.worksheet.Range["A4"])) using (var comWindow = new ComObject<Window>(excelInstance.ActiveWindow)) { comRange.Instance.Select(); comWindow.Instance.FreezePanes = true; } } if (this.fullFilePath != null) { var currentWorkbook = (workbook.Instance as _Workbook); currentWorkbook.SaveAs(this.fullFilePath, XlFileFormat.xlWorkbookNormal); currentWorkbook.Close(false); } } } catch (Exception ex) { System.Diagnostics.Trace.WriteLine(ex.Message); throw; } finally { // Close Excel instance excelInstance.Quit(); } } } } 
+2
Jan 13 '17 at 12:39 on
source share

In case you are desperate . Do not use this approach if you do not understand what it does:

 foreach (Process proc in System.Diagnostics.Process.GetProcessesByName("EXCEL")) { proc.Kill(); } 

Note: this kills every process named "EXCEL".

I had to do this because, despite the fact that I closed every COM object in my code, I still had the stubborn Excel.exe process. Of course, this is by no means the best solution.

+1
Jun 04 '15 at 13:01
source share

Alternatively, you can kill the Excel process as described here .

First import the SendMessage function:

 [DllImport("user32.dll", CharSet = CharSet.Auto)] private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam); 

Then send the WM_CLOSE message to the main window:

 SendMessage((IntPtr)excel.Hwnd, 0x10, IntPtr.Zero, IntPtr.Zero); 
+1
Apr 12 '16 at 11:01
source share

I had the same problem, we can solve the problem without any killing, we always forget to close the interfaces that we used in the Microsoft.Office.Interop.Excel class, so here is a piece of code and follow the structure and the way the objects are cleaned, also keep an eye on the Sheets interface in your code, this is the main culprit that we often close with an application, workbook, books, range, sheet, but we forget or unconsciously do not release the Sheets object or the interface used, so here is the code:

  Microsoft.Office.Interop.Excel.Application app = null; Microsoft.Office.Interop.Excel.Workbooks books = null; Workbook book = null; Sheets sheets = null; Worksheet sheet = null; Range range = null; try { app = new Microsoft.Office.Interop.Excel.Application(); books = app.Workbooks; book = books.Add(); sheets = book.Sheets; sheet = sheets.Add(); range = sheet.Range["A1"]; range.Value = "Lorem Ipsum"; book.SaveAs(@"C:\Temp\ExcelBook" + DateTime.Now.Millisecond + ".xlsx"); book.Close(); app.Quit(); } finally { if (range != null) Marshal.ReleaseComObject(range); if (sheet != null) Marshal.ReleaseComObject(sheet); if (sheets != null) Marshal.ReleaseComObject(sheets); if (book != null) Marshal.ReleaseComObject(book); if (books != null) Marshal.ReleaseComObject(books); if (app != null) Marshal.ReleaseComObject(app); } 
0
Jan 20 '17 at 6:39
source share

@ Denis Molodtsov, trying to be useful, suggested killing all the processes called "EXCEL". This seems to be asking for trouble. There are already many answers that describe how to stop the process after calling excel.quit () while playing with COM interaction. It is better if you can make it work.

@Kevin Vuilleumier was a great suggestion to send WM_CLOSE to an Excel window. I plan to check it out.

If for some reason you need to kill the Excel App Object Excel process, you can specifically target it using something like this:

  using System.Diagnostics; using System.Runtime.InteropServices; // . . . [DllImport("user32.dll", SetLastError=true)] public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint processId); // . . . uint excelAppPid; uint tid = GetWindowThreadProcessId(excel.Hwnd, out excelAppPid); if (tid) { Process excelAppProc = Process.GetProcessById($excelPid) if (excelAppProc) { excelAppProc.Kill() } } 

I don't have time for full testing in C #, but I did a quick test in Powershell where I had a problem with Excel shutting down, and this approach works.

It is pretty simple. Excel Application Object Property The Hwnd property is a handle to a hidden window in an Excel process. Pass excel.Hwnd to GetWindowThreadProcessId to get the process ID. Use this to open the process, finally call Kill ().

At least we are sure that we are killing the right process. Well, pretty sure. If the Excel process has already completed normally, its process ID can be reused by the new process. To limit this possibility, it is important not to wait between the call to excel.quit () and the attempt to destroy.

0
Oct 10 '18 at 21:12
source share



All Articles