1

Given the following code (console application C#) the Word application stops after approximately 300-400 milliseconds, however the Excel application keeps on running. I can't find a way to stop it (without killing the process).

Any suggestions?

using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Diagnostics;
using Microsoft.Office.Interop.Word;
using Microsoft.Office.Interop.Excel;
using Microsoft.Office.Interop.PowerPoint;
using System.Collections.Generic;

namespace test
{

    class Program
    {

        private static int GetProcessIdByWindowTitle(string AppId) 
        {
            Process[] P_CESSES = Process.GetProcesses();
            for (int p_count = 0; p_count < P_CESSES.Length; p_count++) 
            {
                if (P_CESSES[p_count].MainWindowTitle.IndexOf(AppId) != -1) 
                {
                    return P_CESSES[p_count].Id;
                }
            }
            return Int32.MaxValue;
        }

        static void Excel()
        {

            // create the MS-Excel Application
            Microsoft.Office.Interop.Excel.Application excelApp = new Microsoft.Office.Interop.Excel.Application();

            // Set the appID
            string appID = "zizozazezu";
            excelApp.Application.Caption = appID;
            excelApp.Application.Visible = true;

            // Excel needs a workbook to sete a caption visible...
            Microsoft.Office.Interop.Excel.Workbooks wbs = excelApp.Workbooks;
            Microsoft.Office.Interop.Excel.Workbook wb = wbs.Add();

            // find the 
            int processID = GetProcessIdByWindowTitle(appID);

            // verified this is the pid of MS-Excel
            Console.WriteLine(processID);

            // bailing out, no save
            object saveChanges = false;
            ((Microsoft.Office.Interop.Excel._Workbook)wb).Close(saveChanges);
            ((Microsoft.Office.Interop.Excel._Application)excelApp).Quit();

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

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

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

            Console.WriteLine("Excel is done...");

            for (int i=0; i<100; i++)
            {
                // Patience....
                Thread.Sleep(100);

                // still running???
                bool isRunning = false;
                try 
                {
                    Process p = Process.GetProcessById(processID);
                    if (!p.HasExited)
                    {
                        isRunning = true;
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine("{0} Exception caught.", ex);
                }

                if (isRunning) 
                {
                    Console.Write(".");
                }
                else
                {
                    Console.WriteLine("Excel done at i=" + i.ToString());   
                    return;
                }
            }
            return;
        }

        static void Word()
        {
            // create the MS-Word Application 
            Microsoft.Office.Interop.Word.Application wordApp = new Microsoft.Office.Interop.Word.Application();

            // Set a caption - find the right instance
            string appID = "ladadidada";
            wordApp.Caption = appID;
            wordApp.Visible = true;

            // find the 
            int processID = GetProcessIdByWindowTitle(appID);

            // verified this is the pid of MS-Word
            Console.WriteLine(processID);

            // Stop MS-Word
            object saveChanges = false;
            ((Microsoft.Office.Interop.Word._Application)wordApp).Quit(ref saveChanges);

            for (int i=0; i<100; i++)
            {
                // Patience....
                Thread.Sleep(100);

                // still running???
                bool isRunning = false;
                try 
                {
                    Process p = Process.GetProcessById(processID);
                    if (!p.HasExited)
                    {
                        isRunning = true;
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine("{0} Exception caught.", ex);
                }

                if (isRunning) 
                {
                    Console.Write(".");
                }
                else
                {
                    Console.WriteLine("Word done at i=" + i.ToString());    
                    return;
                }
            }
            return;
        }

        static int Main(string[] args)
        {

            Word();
            Excel();

            return -1;
        }
    }
}
Deduplicator
  • 44,692
  • 7
  • 66
  • 118
Paul
  • 1,068
  • 11
  • 29
  • Did you try stepping through the code to see what's happening? – mason Aug 05 '14 at 14:11
  • Yes I did... the process does not terminate/disappear from the process list – Paul Aug 05 '14 at 14:12
  • 3
    I noticed you have `catch {}` in your code. That's a big code smell. You're potentially swallowing exceptions without properly handling them. At the very least you should write the exception to the console. – mason Aug 05 '14 at 14:13
  • You are right, this is the test I've extracted, I've updated the catch, same result – Paul Aug 05 '14 at 14:18
  • 1
    Excel Introp is very awkward and if you don't clean up all the COM objects you reference using Dispose() or usings then the application will not close when you tell it to – AeroX Aug 05 '14 at 14:21
  • 2
    Do. Not. Double. Dot. You're leaking a Workbooks reference at `excelApp.Workbooks.Add(XlWBATemplate.xlWBATWorksheet)`. – CodeCaster Aug 05 '14 at 14:26
  • Word likes to close nice. Excel does not. I have to get the process ID of the excel process and kill it. `IntPtr hwnd = new IntPtr(xApp.Hwnd); IntPtr processID; IntPtr foo = GetWindowThreadProcessId(hwnd, out processID); Process process = Process.GetProcessById(processID.ToInt32()); process.Kill();` `[DllImport("user32.dll")] private static extern IntPtr GetWindowThreadProcessId(IntPtr hWnd, out IntPtr ProcessId);` – Andrew Grinder Aug 05 '14 at 14:28
  • This explains how to fix this: http://stackoverflow.com/a/158752/2594742 – AeroX Aug 05 '14 at 14:30
  • @AndrewGrinder I don't want to kill the process... I want to stop it gently... but thanx – Paul Aug 05 '14 at 14:30
  • @AeroX I'll have a look at it, seems very plausible – Paul Aug 05 '14 at 14:30
  • @Aerox, noop removed the double dot (as far as I can tell), but still no result. – Paul Aug 05 '14 at 15:16
  • @Paul Try changing `excelApp.Application.Caption` to `excelApp.Caption` and `excelApp.Application.Visible` to `excelApp.Visible` as well – AeroX Aug 05 '14 at 15:20
  • Already [covered today](http://stackoverflow.com/a/25135685/17034) (and many times before). – Hans Passant Aug 05 '14 at 15:39

1 Answers1

1

So a few things jump out at me.

  1. You don't call quit on the Excel app itself. That's the signal for it to close.
  2. You also want to make sure you call Close on your workbook.
  3. Make sure you close set all your objects to null when you're done with them
  4. I've also found it's helpful to not only call Marshal.ReleaseComObject but also Marshal.FinalReleaseComObject on all the objects I've used.
  5. You're double-dotting which means you leak a bunch of references. Bad practice, and will encourage Excel to hang around
  6. Finally, to really really REALLY make sure I kill Excel, after I've done all of the above, I kill the process by window handle.

    public static bool TryKillProcessByMainWindowHwnd (int hWnd) {

    uint processID;
    
    GetWindowThreadProcessId((IntPtr)hWnd, out processID);
    
    if (processID == 0) return false;
    
    try
    {
        Process.GetProcessById((int)processID).Kill();
    }
    catch (ArgumentException)
    {
        return false;
    }
    catch (Win32Exception)
    {
        return false;
    }
    catch (NotSupportedException)
    {
        return false;
    }
    catch (InvalidOperationException)
    {
        return false;
    }
    
    return true;
    

    }

In order to make the above work, you also need to make sure you include the Win32 APIs.

[DllImport("user32.dll")]
        private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
Maurice Reeves
  • 1,572
  • 13
  • 19