Search code examples
excelwindows-servicesoffice-interopoffice-automation

Killing EXCEL.exe Process from C# in a Windows Service


I have a windows service that opens up an Excel spreadsheet via the Microsoft.Office.Interop.Excel.Application object.

Application xlApp = new Application();
Workbook workbook = xlApp.Workbooks.Open(fileName, 2, false);
...
...
workbook.Close();
xlApp.Quit();

I would like to kill the EXCEL.exe process that is left running after it is done working with the workbook.

I've tried the following with no success...

// This returns a processId of 0
IntPtr processId;
GetWindowThreadProcessId(new IntPtr(xlApp.Hwnd), out processId);
Process p = Process.GetProcessById(processId.ToInt32());   
p.Kill();

Anyone have any ideas as to how I can do this via a Windows Service?


Solution

  • After much reading and frustration I've found a solution!

    All credit goes to dotNetkow, nightcoder and Mike Rosenblum for their solutions on this post: How do I properly clean up Excel interop objects?

    Here is what I did...
    1. Changed build mode of the project to "Release" (in DEBUG mode, COM objects have a hard time disposing of their references.
    2. Removed all double dot expressions (all COM objects should be tied to a variable so they can be released)
    3. Calling GC.Collect(), GC.WaitForPendingFinalizers(), and Marshal.FinalReleaseComObject() explicitly in a finally block

    Here is the acutal code I am using:

    Application xlApp = null;
    Workbooks workbooks = null;
    Workbook workbook = null;
    Worksheet sheet = null;
    Range r = null;
    object obj = null;
    
    try
    {
        xlApp = new Application();
        xlApp.DisplayAlerts = false;
        xlApp.AskToUpdateLinks = false;
        workbooks = xlApp.Workbooks;
        workbook = workbooks.Open(fileName, 2, false);
        sheet = workbook.Worksheets[1];
    
        r = sheet.get_Range("F19");
        obj = r.get_Value(XlRangeValueDataType.xlRangeValueDefault);
    }
    finally
    {
        GC.Collect();
        GC.WaitForPendingFinalizers();
        if (value != null) Marshal.FinalReleaseComObject(value);
        if (r != null) Marshal.FinalReleaseComObject(r);
        if (sheet != null) Marshal.FinalReleaseComObject(sheet);
        if (workbooks != null) Marshal.FinalReleaseComObject(workbooks);
        if (workbook != null)
        {
            workbook.Close(Type.Missing, Type.Missing, Type.Missing);
            Marshal.FinalReleaseComObject(workbook);
        }
        if (xlApp != null)
        {
            xlApp.Quit();
            Marshal.FinalReleaseComObject(xlApp);
        }
    }