能够关闭除第一个实例之外的所有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进程永远不会被关闭。
我已经发布了一个答案,我自己正在完成这项工作。但是我要把这个问题保留下来,以防有人提出更好的解决方案。
我找到了一个解决这个问题的方法:
[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();
来结束该特定的过程。