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

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

我在这里讨论了与使用GC.Collect命令可以解决的更简单的设置和更少的Excel对象相同的问题。 现在,这些都不起作用。

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

该问题一定与DownloadFile方法的调用有关。 我取消了这个调用,并且Excel进程被正确关闭。 其中一些操作必须保留对其中一个COM对象的引用,以便它们不能关闭。 通过在GarbageCollect之后的最后调用“DownloadFile”,问题得到解决。 (我不太确定为什么)


在你的方法DownloadFile中,你打电话

Response.End()

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

为了模仿ASP中End方法的行为,此方法尝试引发ThreadAbortException异常。 如果此尝试成功, 调用线程将中止 ,[...]

此异常会中止您的线程。 因此,你所有的ReleaseComObject,Excel.Quit,GC.Collect都不会被执行。

解决方案:不要调用Response.End 。 你可能不需要它。 如果您需要它,您可能需要考虑文档中提到的替代方案:

此方法仅用于与ASP兼容,即为了与ASP.NET之前的基于COM的Web编程技术兼容。 如果您想要跳转到EndRequest事件并向客户端发送响应,通常最好调用CompleteRequest。

[...]

CompleteRequest方法不会引发异常,并且可能会执行对CompleteRequest方法的调用之后的代码


PS:从Web应用程序使用Excel自动化不是由Microsoft正式支持。 对于将来的开发,您可能需要考虑使用第三方Excel库。


我发现有时唯一有帮助的是“大锤法”。 杀死所有正在运行的Excel实例:

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

在我看来,你有一个参考没有清理。 可能是像'两点规则'这样的问题 - 在我看来这是一个愚蠢的规则,因为你无法编码任何正派的东西,因为它难以跟踪。

你可以尝试你的COM引用的Marshal.ReleaseComObject,但仍然要求麻烦...

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

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

链接地址: http://www.djcxy.com/p/35667.html

上一篇: Excel Instance wont close after Interop Operations

下一篇: Closing Excel application with Excel Interop without save message