【问题标题】:Excel Instance wont close after Interop Operations互操作操作后 Excel 实例不会关闭
【发布时间】:2016-01-04 13:16:28
【问题描述】:

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

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

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”,问题得到解决。 (我不太清楚为什么)

【问题讨论】:

  • 你试过杀死那个excel进程msdn.microsoft.com/en-us/library/…
  • 您是否阅读过stackoverflow.com/questions/158706/… 并尝试了那里的所有方法?如果是,请创建minimal reproducible example 来重现该问题(您的代码很短,但显然还不是最小的)。
  • @Ali Hasan:杀死进程应该是最后的资源;我正在与可能的当代用户一起运行一个 Web 应用程序,因此杀死任何 excel 实例都不是一个好的解决方案。
  • @Heinzi:我仔细检查了该链接中已详细解释的 2 点规则。构建一个最小的示例使我找到了解决方案,也非常感谢您:删除 DownloadFile 调用使 Excel 进程消失!
  • @Barnabeck:是的,极少的例子有时会对你产生影响。 ;-) 下载文件?但是为什么...啊,现在我明白了为什么会发生这种情况。我会写一个关于它的答案。

标签: c# excel interop


【解决方案1】:

在你的方法 DownloadFile 中,你调用

Response.End()

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

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

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

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

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

[...]

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


PS:在 Web 应用程序中使用 Excel 自动化是 not officially supported by Microsoft。对于未来的开发,您可能需要考虑改用第三方 Excel 库。

【讨论】:

  • 很好的解释!现在一切都清楚了,正如你所说,我不需要 Response.End。我经常读到,Microsoft 不支持 Web 应用程序中的 Excel 自动化,但现在一切都按我想要的方式运行,我为什么要关心? ;)
【解决方案2】:

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

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

【讨论】:

  • 同阿里哈桑:杀死进程应该是最后的资源;我正在与可能的当代用户一起运行一个 Web 应用程序,因此杀死任何 excel 实例都不是一个好的解决方案。
【解决方案3】:

在我看来,您有一个未清理的参考。可能类似于“两点规则”问题 - 在我看来这是一个愚蠢的规则,因为您无法编写任何像样的代码,因为它很难跟踪。

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

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

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

【讨论】:

  • 这不是“2 点规则”,DownloadFile 方法的调用 - 如上所述 - 一定会保留一些引用,尽管我不明白为什么。
  • 是的,我相信你:)
  • 是的,我相信你 :) 我的意思是它会是这样的。每次您横切 Excel 对象模型时,都会返回各种很容易遗漏的对象。尽管您最终会通过反复试验和各种 ReleaseCOMObjects 等找到修复程序,但如果您稍后更改某些内容,它可能会再次发生。事实上,您可以检查 ReleaseCOMObjects 的返回值以获取引用计数,但这并不可靠。如果您使用 VSTO,这将在应用程序域被拉下时被清除。您当然可以尝试创建自己的应用程序域..
猜你喜欢
  • 1970-01-01
  • 2014-12-02
  • 1970-01-01
  • 2013-02-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多