Excel 实例不会在互操作操作后关闭

Excel Instance wont close after Interop Operations

我正在通过合并一系列不同 Excel 工作簿中的第一个 sheet,用 c# 构建一个新的 Excel 工作簿;随后我将新工作簿导出为 PDF。我完成了这项工作,但在方法结束时总是有一个 Excel 实例 运行。

我讨论了同样的问题 ,但设置更简单,更少我可以使用 GC.Collect 命令解决的 Excel 个对象。现在,none 正在运行。

public void CombineWorkBooks()
    {
        Microsoft.Office.Interop.Excel.Application xlApp = new Microsoft.Office.Interop.Excel.Application();

        xlApp.DisplayAlerts = false;
        xlApp.Visible = false;

        Workbooks newBooks = null;
        Workbook newBook = null;
        Sheets newBookWorksheets = null;
        Worksheet defaultWorksheet = null;

        // Create a new workbook, comes with an empty default worksheet");
        newBooks = xlApp.Workbooks;

        newBook = newBooks.Add(XlWBATemplate.xlWBATWorksheet);
        newBookWorksheets = newBook.Worksheets;

        // get the reference for the empty default worksheet
        if (newBookWorksheets.Count > 0)
        {
            defaultWorksheet = newBookWorksheets[1] as Worksheet;
        }

        // loop through every line in Gridview and get the path' to each Workbook
        foreach (GridViewRow row in CertificadosPresion.Rows)
        {
            string path = row.Cells[0].Text;
            string CertName = CertificadosPresion.DataKeys[row.RowIndex].Value.ToString();

            Workbook childBook = null;
            Sheets childSheets = null;

            // Excel of each line in Gridview
            childBook = newBooks.Open(path,Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);
            childSheets = childBook.Worksheets;

            if (childSheets != null)
            {
                // Build a new Worksheet
                Worksheet sheetToCopy = null;

                // Only first Worksheet of the Workbook belonging to that line
                sheetToCopy = childSheets[1] as Worksheet;
                if (sheetToCopy != null)
                {
                    // Assign the Certificate Name to the new Worksheet 
                    sheetToCopy.Name = CertName;
                    // set PageSetup for the new Worksheet to be copied
                    sheetToCopy.PageSetup.Zoom = false;
                    sheetToCopy.PageSetup.FitToPagesWide = 1;
                    sheetToCopy.PageSetup.FitToPagesTall = 1;
                    sheetToCopy.PageSetup.PaperSize = Microsoft.Office.Interop.Excel.XlPaperSize.xlPaperA4;
                    // Copy that new Worksheet to the defaultWorksheet
                    sheetToCopy.Copy(defaultWorksheet, Type.Missing);
                }
                System.Runtime.InteropServices.Marshal.ReleaseComObject(sheetToCopy);
                childBook.Close(false, Type.Missing, Type.Missing);
                
            }
            System.Runtime.InteropServices.Marshal.ReleaseComObject(childSheets);
            System.Runtime.InteropServices.Marshal.ReleaseComObject(childBook);
        }

        //Delete the empty default worksheet
        if (defaultWorksheet != null) defaultWorksheet.Delete();

        //Export to PDF
        newBook.ExportAsFixedFormat(Microsoft.Office.Interop.Excel.XlFixedFormatType.xlTypePDF, @"C:\pdf\" + SALESID.Text + "_CertPres.pdf", 0, false, true);

        newBook.Close();
        newBooks.Close();
        xlApp.DisplayAlerts = true;
        DownloadFile(SALESID.Text);

        System.Runtime.InteropServices.Marshal.ReleaseComObject(defaultWorksheet);
        System.Runtime.InteropServices.Marshal.ReleaseComObject(newBookWorksheets);
        System.Runtime.InteropServices.Marshal.ReleaseComObject(newBook);
        System.Runtime.InteropServices.Marshal.ReleaseComObject(newBooks);

        xlApp.Quit();
        System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp);

        GC.Collect();
        GC.WaitForPendingFinalizers();
    }

        protected void DownloadFile(string Salesid)
    {
        string path = @"c:\pdf\" + Salesid + "_CertPres.pdf";
        byte[] bts = System.IO.File.ReadAllBytes(path);
        Response.Clear();
        Response.ClearHeaders();
        Response.AddHeader("Content-Type", "Application/octet-stream");
        Response.AddHeader("Content-Length", bts.Length.ToString());
        Response.AddHeader("Content-Disposition", "attachment; filename=" + Salesid + "_CertPres.pdf");
        Response.BinaryWrite(bts);
        Response.Flush();
        Response.End();
    }

问题一定与调用下载文件方法有关。我消除了那个调用,并且 Excel 进程被正确关闭。其中一些操作必须保持对其中一个 COM 对象的引用处于打开状态,以便它们无法关闭。通过在 GarbageCollect 之后的最后调用“DownloadFile”,问题就解决了。 (我不太清楚为什么)

我发现有时唯一有用的是 "sledgehammer method"。杀死所有 运行 excel 个实例:

foreach (Process p in Process.GetProcessesByName("EXCEL"))
{
    try
    {
        p.Kill();
        p.WaitForExit();
    }
    catch
    {
        //Handle exception here
    }
}

在我看来,您有一个未清理的参考。可能类似于 'two dot rule' 问题 - 在我看来这是一个愚蠢的规则,因为你不能编写任何像样的代码,因为它很难跟踪。

您可以尝试 Marshal.ReleaseComObject 个 COM 引用,但仍然自找麻烦...

我的建议是尝试使用 VSTO 实现自动化 Excel。这将代表您正确清除您的引用。

https://social.msdn.microsoft.com/Forums/vstudio/en-US/a12add6b-99ea-4677-8245-cd667101683e/vsto-and-office-objects-disposing

在您的方法 DownloadFile 中,您调用

Response.End()

HttpResponse.End 抛出异常(强调我的):

To mimic the behavior of the End method in ASP, this method tries to raise a ThreadAbortException exception. If this attempt is successful, the calling thread will be aborted, [...]

此异常中止您的线程。因此,您所有的 ReleaseComObject,Excel.Quit,GC.Collect 东西 永远不会执行

解决方法:不要调用 Response.End。你可能不需要它。如果需要,您可能需要考虑替代方案 mentioned in the documentation

This method is provided only for compatibility with ASP—that is, for compatibility with COM-based Web-programming technology that preceded ASP.NET. If you want to jump ahead to the EndRequest event and send a response to the client, it is usually preferable to call CompleteRequest instead.

[...]

The CompleteRequest method does not raise an exception, and code after the call to the CompleteRequest method might be executed


PS:从 Web 应用程序使用 Excel 自动化是 not officially supported by Microsoft。对于未来的开发,您可能需要考虑使用 third-party Excel 库。