添加新工作表到现有文件后C#interop:excel进程不退出

可能重复:
如何正确清理C#中的Excel互操作对象

我已经阅读了许多关于pipe理COM引用的其他线程,同时使用.Net-Excel互操作来确保Excel进程在退出时正确退出,到目前为止技术运行良好,但是最近我遇到了将新工作表添加到现有工作簿文件时出现问题。

下面的代码留下了一个僵尸Excel过程。

如果我将工作表添加到新创build的工作簿文件,它退出罚款。 如果我运行的代码不包括.Add()行,它退出罚款。 (我正在阅读的现有文件是由注释掉的代码创build的空文件)

有任何想法吗?

 //using Excel = Microsoft.Office.Interop.Excel; //using System.Runtime.InteropServices; public static void AddTest() { string filename = @"C:\addtest.xls"; object m = Type.Missing; Excel.Application excelapp = new Excel.Application(); if (excelapp == null) throw new Exception("Can't start Excel"); Excel.Workbooks wbs = excelapp.Workbooks; //if I create a new file and then add a worksheet, //it will exit normally (ie if you uncomment the next two lines //and comment out the .Open() line below): //Excel.Workbook wb = wbs.Add(Excel.XlWBATemplate.xlWBATWorksheet); //wb.SaveAs(filename, m, m, m, m, m, // Excel.XlSaveAsAccessMode.xlExclusive, // m, m, m, m, m); //but if I open an existing file and add a worksheet, //it won't exit (leaves zombie excel processes) Excel.Workbook wb = wbs.Open(filename, m, m, m, m, m, m, Excel.XlPlatform.xlWindows, m, m, m, m, m, m, m); Excel.Sheets sheets = wb.Worksheets; //This is the offending line: Excel.Worksheet wsnew = sheets.Add(m, m, m, m) as Excel.Worksheet; //NB it doesn't help if I try specifying the parameters in Add() above wb.Save(); wb.Close(m, m, m); //overkill to do GC so many times, but shows that doesn't fix it GC(); //cleanup COM references //changing these all to FinalReleaseComObject doesn't help either while (Marshal.ReleaseComObject(wsnew) > 0) { } wsnew = null; while (Marshal.ReleaseComObject(sheets) > 0) { } sheets = null; while (Marshal.ReleaseComObject(wb) > 0) { } wb = null; while (Marshal.ReleaseComObject(wbs) > 0) { } wbs = null; GC(); excelapp.Quit(); while (Marshal.ReleaseComObject(excelapp) > 0) { } excelapp = null; GC(); } public static void GC() { System.GC.Collect(); System.GC.WaitForPendingFinalizers(); System.GC.Collect(); System.GC.WaitForPendingFinalizers(); } 

我没有这个代码,但是我遇到了类似的问题。 如果我记得正确的话,我最终检索到excel实例的进程id,然后杀死它(经过一段合适的等待时间后,另一个方法失败)。

我想我用了:

GetWindowThreadProcessId (通过P / Invoke)对excel对象的hwnd属性获取进程id,然后使用Process.GetProcessById获取一个进程对象。 一旦我完成了,我会打电话给Kill

编辑:我不得不承认,这不是理想的解决scheme,但如果你不能find未被释放的stream氓接口,那么这将以真正的蛋壳/大锤的方式修复它。 ;)

编辑2:你不必立即调用过程对象的Kill …你可以先诉诸于Kill前先调用Close

我也做了类似的事情。 我创build一个Excel文件或打开一个现有的。 我删除所有的表格,并添加我自己的。 这里是我用来确保所有引用都closures的代码:

  workbook.Close(true, null, null); excelApp.Quit(); if (newSheet != null) { System.Runtime.InteropServices.Marshal.ReleaseComObject(newSheet); } if (rangeSelection != null) { System.Runtime.InteropServices.Marshal.ReleaseComObject(rangeSelection); } if (sheets != null) { System.Runtime.InteropServices.Marshal.ReleaseComObject(sheets); } if (workbook != null) { System.Runtime.InteropServices.Marshal.ReleaseComObject(workbook); } if (excelApp != null) { System.Runtime.InteropServices.Marshal.ReleaseComObject(excelApp); } newSheet = null; rangeSelection = null; sheets = null; workbook = null; excelApp = null; GC.Collect(); 

我已经testing了很多不同的选项,并没有让我失败。

这里是我完整的代码来杀死您使用Office12 .Net互操作库创build的Excel:享受, – 阿兰。

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.InteropServices; using System.Diagnostics; using Microsoft.Office.Interop.Excel; class Program { /// <summary> /// Win32 API import for getting the process Id. /// The out param is the param we are after. I have no idea what the return value is. /// </summary> [DllImport("user32.dll")] private static extern IntPtr GetWindowThreadProcessId(IntPtr hWnd, out IntPtr ProcessId); static void Main(string[] args) { var app = new Application(); IntPtr hwnd = new IntPtr(app.Hwnd); IntPtr processId; IntPtr foo = GetWindowThreadProcessId(hwnd, out processId); Process proc = Process.GetProcessById(processId.ToInt32()); proc.Kill(); // set breakpoint here and watch the Windows Task Manager kill this exact EXCEL.EXE app.Quit(); // should give you a "Sorry, I can't find this Excel session since you killed it" Exception. } } 

这对我来说很好,没有任何例外。

 Public Class ExcelHlpr Declare Function EndTask Lib "user32.dll" (ByVal hWnd As IntPtr, ByVal ShutDown As Boolean, ByVal Force As Boolean) As Integer Dim cXlApp As Microsoft.Office.Interop.Excel.Application Public Function GetExcel() As Microsoft.Office.Interop.Excel.Application cXlApp = New Microsoft.Office.Interop.Excel.Application Return cXlApp End Function Public Function EndExcel() As Integer Dim xlHwnd As New IntPtr(cXlApp.Hwnd) Return EndTask(xlHwnd, False, True) End Function End Class 

我不是非常有build设性,但是我完全按照上面所示testing了代码,并且我的Excel进程按预期退出,我的C:\ addtest.xls正在坐着8张新的工作表,并且没有Excel进程正在运行。
互操作性版本可能是我想知道的原因吗? 我testing了11和12。

我正在使用VB.NET 3.5 SP1和下面的代码仍然保持EXCEL.EXE打开:

  xlWorkbook.Close(SaveChanges:=False) xlApplication.Quit() System.Runtime.InteropServices.Marshal.ReleaseComObject(xlRange) System.Runtime.InteropServices.Marshal.ReleaseComObject(xlWorksheet) System.Runtime.InteropServices.Marshal.ReleaseComObject(xlSheets) System.Runtime.InteropServices.Marshal.ReleaseComObject(xlWorkbook) System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApplication) xlRange = Nothing xlWorksheet = Nothing xlSheets = Nothing xlWorkbook = Nothing xlApplication = Nothing GC.GetTotalMemory(False) GC.Collect() GC.WaitForPendingFinalizers() GC.Collect() GC.WaitForPendingFinalizers() GC.Collect() GC.GetTotalMemory(True) 

安德鲁,这是我发现的作品的代码。 我以为我发布在这里为其他人遇到:

 namespace WindowHandler { using System; using System.Text; using System.Collections; using System.Runtime.InteropServices; /// <summary> /// Window class for handling window stuff. /// This is really a hack and taken from Code Project and mutilated to this small thing. /// </summary> public class Window { /// <summary> /// Win32 API import for getting the process Id. /// The out param is the param we are after. I have no idea what the return value is. /// </summary> [DllImport("user32.dll")] private static extern IntPtr GetWindowThreadProcessId(IntPtr hWnd, out IntPtr ProcessId); /// <summary> /// Gets a Window's process Id. /// </summary> /// <param name="hWnd">Handle Id.</param> /// <returns>ID of the process.</returns> public static IntPtr GetWindowThreadProcessId(IntPtr hWnd) { IntPtr processId; IntPtr returnResult = GetWindowThreadProcessId(hWnd, out processId); return processId; } } }