C# interop: excel process not exiting after adding new worksheet to existing file

27,019

Solution 1

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 used Process.GetProcessById to get a process object. Once I'd done that, I'd call Kill on 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 Kill on the process object immediately... You could first try calling Close before resorting to Kill.

Solution 2

I have done a similar thing. I create an Excel file or open an existing. I delete all the sheets and add my own. here is the code I use to ensure all references are closed:

            workbook.Close(true, null, null);
            excelApp.Quit();

            if (newSheet != null)
            {
                System.Runtime.InteropServices.Marshal.ReleaseComObject(newSheet);
            }
            if (rangeSelection != null)
            {
            System.Runtime.InteropServices.Marshal.ReleaseComObject(rangeSelection);
            }
            if (sheets != null)
            {
                System.Runtime.InteropServices.Marshal.ReleaseComObject(sheets);
            }
            if (workbook != null)
            {
                System.Runtime.InteropServices.Marshal.ReleaseComObject(workbook);
            }
            if (excelApp != null)
            {
                System.Runtime.InteropServices.Marshal.ReleaseComObject(excelApp);
            }

            newSheet = null;
            rangeSelection = null;
            sheets = null;
            workbook = null;
            excelApp = null;

            GC.Collect();

I have tested this with many different options and not had it fail on me yet.

Solution 3

here's my full code to kill the Excel you created with the Office12 .Net interop library: Enjoy, -Alan.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Diagnostics;
using Microsoft.Office.Interop.Excel;

class Program
{

    /// <summary> 
    /// Win32 API import for getting the process Id. 
    /// The out param is the param we are after. I have no idea what the return value is. 
    /// </summary> 
    [DllImport("user32.dll")]
    private static extern IntPtr GetWindowThreadProcessId(IntPtr hWnd, out IntPtr ProcessId); 

    static void Main(string[] args)
    {
        var app = new Application();
        IntPtr hwnd = new IntPtr(app.Hwnd);
        IntPtr processId;
        IntPtr foo = GetWindowThreadProcessId(hwnd, out processId);
        Process proc = Process.GetProcessById(processId.ToInt32());
        proc.Kill(); // set breakpoint here and watch the Windows Task Manager kill this exact EXCEL.EXE
        app.Quit(); // should give you a "Sorry, I can't find this Excel session since you killed it" Exception.
    }
}

Solution 4

This is works very fine for me, without any exceptions.

Public Class ExcelHlpr

    Declare Function EndTask Lib "user32.dll" (ByVal hWnd As IntPtr, ByVal ShutDown As Boolean, ByVal Force As Boolean) As Integer

    Dim cXlApp As Microsoft.Office.Interop.Excel.Application

    Public Function GetExcel() As Microsoft.Office.Interop.Excel.Application
        cXlApp = New Microsoft.Office.Interop.Excel.Application
        Return cXlApp
    End Function

    Public Function EndExcel() As Integer
        Dim xlHwnd As New IntPtr(cXlApp.Hwnd)
        Return EndTask(xlHwnd, False, True)
    End Function

End Class
Share:
27,019
yoyoyoyosef
Author by

yoyoyoyosef

Updated on July 29, 2022

Comments

  • yoyoyoyosef
    yoyoyoyosef almost 2 years

    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();
    }
    
  • Simon Wilson
    Simon Wilson over 15 years
    Can you get other guys in your shop replicate it? I just had 2 other guys stop there work :) and try with the no orphaned processes.
  • Simon Wilson
    Simon Wilson over 15 years
    By "there" I meant "their", me English not gud
  • yoyoyoyosef
    yoyoyoyosef over 15 years
    yep, replicated here on a different machine. Both are using PIA dll version 11.0.0.0, one with Excel 2003 SP2 (11.6560.6568) installed, the other with Excel 2003 SP3 (11.8231.8221).
  • Simon Wilson
    Simon Wilson over 15 years
    Just a thought here. Ensure you kill al Excel process running then add a try{}finally{} block after Excel.Application excelapp = new Excel.Application() and stick the excelApp cleanup in he finally and see if it is still happening. I know...but give it a shot.
  • thiag0
    thiag0 about 13 years
    I am using your code and getting back a processId of zero. Any ideas of what could cause this to happen?