0

I am trying to close excel process in my winform application. I have gone through lots of posts on SO and other sites and this is what I am doing right now:

private void lsvSelectedQ_SelectedIndexChanged(object sender, EventArgs e)
        {
          FillSelectedItems();
        }

private void FillSelectedItems()
        {
            string filepath = string.Empty;
            string reportname = lblreportname.Text;
            filepath = Application.StartupPath + "\\StandardReports\\" + reportname + ".xls";

            Microsoft.Office.Interop.Excel.Application xlApp;
            Microsoft.Office.Interop.Excel.Workbook xlWorkBook;
            Microsoft.Office.Interop.Excel.Worksheet xlWorkSheet;
            object misValue = System.Reflection.Missing.Value;

            xlApp = new Microsoft.Office.Interop.Excel.Application();
            xlWorkBook = xlApp.Workbooks.Open(filepath, 0, true, 5, "", "", true, Microsoft.Office.Interop.Excel.XlPlatform.xlWindows, "\t", false, false, 0, true, 1, 0);
            List<string> WorksheetList = new List<string>();          

            xlWorkSheet = (Microsoft.Office.Interop.Excel.Worksheet)xlWorkBook.Worksheets.get_Item("Config");

            Microsoft.Office.Interop.Excel.Range objRange = null;

            objRange = (Microsoft.Office.Interop.Excel.Range)xlWorkSheet.Cells[6, 2];
            if (objRange.Value != null)
            {
                int intSheet =  Convert.ToInt32(objRange.Value);

                for (int i = 0; i < intSheet; i++)
                {
                    objRange = (Microsoft.Office.Interop.Excel.Range)xlWorkSheet.Cells[6, 3+i ];
                    if (objRange.Value != null)
                    {
                        lsvSelectedQ.Items.Add(Convert.ToString(objRange.Value));
                    }
                }
            }           

           ReleaseMyExcelsObjects(xlApp, xlWorkBook, xlWorkSheet);           
        }

In the above code I am using ReleaseMyExcelsObjects method to get rid of running excel process in the taskbar.

  private void ReleaseMyExcelsObjects(Microsoft.Office.Interop.Excel.Application xlApp, Microsoft.Office.Interop.Excel.Workbook xlWorkBook, Microsoft.Office.Interop.Excel.Worksheet xlWorkSheet)
        {
            xlApp.DisplayAlerts = false;
            xlWorkBook.Save();
            xlWorkBook.Close();
            xlApp.DisplayAlerts = false;
            xlApp.Quit();
            System.Runtime.InteropServices.Marshal.ReleaseComObject(xlWorkSheet);
            System.Runtime.InteropServices.Marshal.ReleaseComObject(xlWorkBook);
            System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp);
            xlWorkSheet = null;
            xlWorkBook = null;
            xlApp = null;
            GC.Collect();
        }

As you can see I open an excel on a SelectedIndexChanged and I am also trying to close the process through ReleaseMyExcelsObjects() method and it works except for the first excel process that is generated. I mean when the event is fired the excel process is started and ReleaseMyExcelsObjects() does not close it however the second time SelectedIndexChanged is fired, another excel process is started. This time ReleaseMyExcelsObjects() closes this second excel process. But the first excel process which was started when theSelectedIndexChanged event was fired for the first time never gets closed.

EDIT:

I have posted an answer myself which is getting the job done. But I am going to keep this question open if in case someone comes up with better solution.

SamuraiJack
  • 5,131
  • 15
  • 89
  • 195

3 Answers3

0

from my understanding, the ReleaseComObject closes the Excel Application when your C# application exits. Thus, a new Excel processes will show up in your taskbar every time you call FillSelectedItems() which won't close until you exit your C# application.

EDIT: On a side note, I recommend using try, catch, finally when handling the excel interop library, mainly due to the fact that if the application runs into an exception, the program will not exit normally, and as stated before it will result on the excel process remaining in the taskbar (and when you manually open said file on excel it will tell you the last recovered version blablabla)

string filepath = string.Empty;
        string reportname = lblreportname.Text;
        filepath = Application.StartupPath + "\\StandardReports\\" + reportname + ".xls";

        Microsoft.Office.Interop.Excel.Application xlApp;
        Microsoft.Office.Interop.Excel.Workbook xlWorkBook;
        Microsoft.Office.Interop.Excel.Worksheet xlWorkSheet;
        object misValue = System.Reflection.Missing.Value;

        xlApp = new Microsoft.Office.Interop.Excel.Application();
        xlWorkBook = xlApp.Workbooks.Open(filepath, 0, true, 5, "", "", true, Microsoft.Office.Interop.Excel.XlPlatform.xlWindows, "\t", false, false, 0, true, 1, 0);
        List<string> WorksheetList = new List<string>();

        try
        {
            //Your code
        }
        catch (Exception ex) { MessageBox.Show(string.Format("Error: {0}", ex.Message)); }
        finally
        {
            xlWorkBook.Close(false, filepath, null);
            Marshal.ReleaseComObject(xlWorkBook);
        }

Honestly, if you are annoyed by this, I would recommend using EPPlus or ExcelDataReader(this one only for reading the excel spreadsheets) as alternative libraries. Otherwise, No matter how many releaseComObjects or garbagecollectors you add, I believe you wont get rid of this issue.

EDIT 2: Of course, another way to go around this is to search for an Excel process ID and kill it. The reason I did not recommend this, is because in the event that the user who's running this application already has another excel process running on his computer, you could end up killing that instance.

Innat3
  • 3,561
  • 2
  • 11
  • 29
  • But the above code IS closing excel application each and everytime `FillSelectedItems() ` is called. Everytime except the first time. – SamuraiJack Sep 01 '16 at 07:21
  • It makes the closing call, but doesnt get applied until the C# application exits. You can check it in the taskbar by calling FillSelectedItems() multiple times. You will see that a new "Excel" process is visible after each call. – Innat3 Sep 01 '16 at 07:25
  • Everytime `FillSelectedItems()` is called and instance of excel process is started. If the event is fired 3 times there will be 3 excel processes in the taskmanager but `ReleaseMyExcelsObjects` closes the new instances everytime. EXCEPT the first time. (without having to exit the application) – SamuraiJack Sep 01 '16 at 07:33
0

I have found a work around for this:

   [System.Runtime.InteropServices.DllImport("user32.dll")]
        static extern int GetWindowThreadProcessId(int hWnd, out int lpdwProcessId);

        public static Process GetExcelProcess(Microsoft.Office.Interop.Excel.Application excelApp)
        {
            int id;
            GetWindowThreadProcessId(excelApp.Hwnd, out id);
            return Process.GetProcessById(id);
        }

Use it as classobj.GetExcelProcess(xlApp).Kill();

Taken from : https://stackoverflow.com/a/15556843/2064292

Community
  • 1
  • 1
SamuraiJack
  • 5,131
  • 15
  • 89
  • 195
0

I have a solution for closing the Excel process. Instead of going thru the pains of releasing your objects, you can kill that specific excel process (if you have multiple of them open). The code is:

using System;
using System.Runtime.InteropServices;
using Microsoft.Office.Interop.Excel;

namespace YourNameSpace
{
    public class MicrosoftApplications
    {
        [DllImport("user32.dll")]
        static extern int GetWindowThreadProcessId(int hWnd, out int lpdwProcessId);
        public class Excel
        {   
            public Excel()
            {
                Application = new Microsoft.Office.Interop.Excel.Application();
                RegisterExitEvent();
            }

            public Microsoft.Office.Interop.Excel.Application Application;
            
            private void RegisterExitEvent()
            {
                Application.WindowDeactivate -= XlApp_WindowDeactivate;
                Application.WindowDeactivate += XlApp_WindowDeactivate;
            }

            private void XlApp_WindowDeactivate(Workbook Wb, Window Wn)
            {
                Kill();
            }

            public void Kill()
            {
                int pid = 0;
                GetWindowThreadProcessId(Application.Hwnd, out pid);
                if (pid > 0)
                {
                    System.Diagnostics.Process p = System.Diagnostics.Process.GetProcessById(pid);
                    p.Kill();
                }
                Application = null;
                GC.Collect();
                GC.WaitForPendingFinalizers();
            }
        }
    }
}

And you can call it by: YourNameSpace.MicrosoftApplications.Excel xlApp = new YourNameSpace.MicrosoftApplications.Excel();

Do whatever you need to do by calling xlApp.Application.whatever instead of xlApp.whatever and if the user exits the excel window(s) it will kill the process(es) that were used in the code. If you want to just generate a report behind the scenes but not display the form, then simply call xlApp.Kill(); to end that specific process.