能够关闭除第一个实例之外的所有excel进程

本文关键字:进程 excel 实例 第一个 | 更新日期: 2023-09-27 18:05:09

我正在尝试关闭我的winform应用程序中的excel进程。我已经浏览了SO和其他网站上的很多帖子,这就是我现在正在做的:

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);           
        }

在上面的代码中,我使用ReleaseMyExcelsObjects方法来摆脱在任务栏中运行excel进程。

  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();
        }

正如你所看到的,我在SelectedIndexChanged上打开一个excel,我也试图通过ReleaseMyExcelsObjects()方法关闭该进程,除了生成的第一个excel进程外,它还有效。我的意思是,当事件被触发时,excel进程启动,ReleaseMyExcelsObjects()没有关闭它,但是第二次SelectedIndexChanged被触发,另一个excel进程启动。这次ReleaseMyExcelsObjects()关闭了第二个excel进程。但是当theSelectedIndexChanged事件第一次被触发时启动的第一个excel进程永远不会被关闭。

编辑:

我已经发布了一个答案,我自己正在完成这项工作。但是我要把这个问题保留下来,以防有人提出更好的解决方案。

能够关闭除第一个实例之外的所有excel进程

我找到了一个解决这个问题的方法:

   [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);
        }

使用classobj.GetExcelProcess(xlApp).Kill();

摘自:https://stackoverflow.com/a/15556843/2064292

根据我的理解,当c# 应用程序退出时,ReleaseComObject关闭Excel应用程序。因此,每当你调用FillSelectedItems()时,一个新的Excel进程将显示在你的任务栏中,直到你退出你的c#应用程序。

编辑:顺便说一句,我建议在处理excel互操作库时使用try, catch, finally,主要是因为如果应用程序运行异常,程序将无法正常退出,并且如前所述,它将导致excel进程留在任务栏中(当你在excel上手动打开该文件时,它会告诉你最后恢复的版本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);
        }

老实说,如果你对此感到烦恼,我建议使用EPPlus或ExcelDataReader(这个只用于阅读excel电子表格)作为替代库。否则,无论你添加多少个releaseComObjects或垃圾收集器,我相信你都无法摆脱这个问题。

编辑2:当然,另一种解决这个问题的方法是搜索一个Excel进程ID并杀死它。我不推荐这样做的原因是,如果运行此应用程序的用户在其计算机上已经运行了另一个excel进程,那么最终可能会杀死该实例。

我有一个关闭Excel进程的解决方案。而不是经历释放你的对象的痛苦,你可以杀死特定的excel进程(如果你有多个打开)。代码是:

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();
            }
        }
    }
}

可以这样调用:YourNameSpace.MicrosoftApplications.Excel xlApp = new YourNameSpace.MicrosoftApplications.Excel();

通过调用xlApp.Application.whatever而不是xlApp.whatever来做任何你需要做的事情,如果用户退出excel窗口,它将杀死代码中使用的进程。如果您只想在后台生成一个报告,而不显示表单,那么只需调用xlApp.Kill();来结束该特定的过程。