【问题标题】:Unable to close excel even after releasing resources释放资源后也无法关闭excel
【发布时间】:2017-07-08 16:10:51
【问题描述】:

我尝试了很多解决方案,但都没有奏效,请帮我解决这个问题。 以下是SSIS Script Task

中使用的代码
    using Excel = Microsoft.Office.Interop.Excel;        
            Excel.Application xlApp = null; 
            Excel.Workbooks workbooks = null;
            Excel.Workbook xlWorkbook = null;
            Excel.Worksheet worksheet = null;
            Excel.Range xlRange = null;                           
        try
          {

            xlApp = new Excel.Application();
            xlApp.DisplayAlerts = false;
            xlApp.AskToUpdateLinks = false;
            workbooks = xlApp.Workbooks;
            xlWorkbook = workbooks.Open("sample.csv", 2, true);
            xlWorksheet = xlWorkbook.Sheets[1];
            xlRange = xlWorksheet.UsedRange;
            int rowCount = xlRange.Rows.Count;
            for (int row = 2; row <= rowCount; row++)
            {
              //some logic

            }
           }
    catch (Exception ex)
    {
       MessageBox.Show(ex.Message);
    }
    finally  //releasing all resources
    {
       GC.Collect();
       GC.WaitForPendingFinalizers();
       Marshal.ReleaseComObject(xlRange);
       Marshal.ReleaseComObject(xlWorksheet);
       xlRange = null;
       xlWorksheet = null;
       xlWorkbook.Close();
       Marshal.ReleaseComObject(xlWorkbook);
       xlWorkbook = null;
       workbooks = null;
       xlApp.Quit();
       Marshal.ReleaseComObject(xlApp);
       xlApp=null;
}

即使释放资源后,仍然看到一个excel进程

【问题讨论】:

    标签: c# excel ssis etl excel-interop


    【解决方案1】:

    看起来你走在正确的轨道上。但是,您当前是 ExcelApplication,即您将在任务监视器中看到的实际进程,以及在 WordApplication 被杀死之前需要关闭的 ExcelDocument。

    看看这个简单的例子:

    public void Foo(string filePath)
    {
        var application = new Excel.Application();
    
        // do your stuff here...
        var document = application.Workbooks.Open(filePath, ReadOnly: true, Visible: false);
    
        // this is the magic:  clear 'application' and 'document',
        // once the work is finished
        if (document != null)
            document.Close(SaveChanges: false);
    
        if (application != null)
            application.Quit(SaveChanges: false);
    
        document = null;
        application = null;
    }
    

    在这里您会看到我们实际上正确地Close() 工作簿(文档)和Quit() 应用程序。这有点像您在 finally 部分中所做的事情,但请记住,变量是在 之前初始化的,您可以进入 try... 部分。

    我的建议是:不要手动调用任何垃圾收集 (GC) 和 Marshal 的东西。如果你不确定它的作用,它会在以后伤害你并产生副作用。接下来,您可以通过在 ExcelApplication 和 ExcelWorkbook 周围添加一个漂亮的 using (...) 结构来进一步改进示例。

    【讨论】:

      【解决方案2】:

      正确清理Interop.Excel 对象很难。做错了,你可能会因为大量的 Excel 僵尸实例而使 SQL 或 SSIS 机器陷入困境。

      互联网是litteredwithadvice关于如何正确Close(),Quit(),ReleaseObject()Interop.Excel对象。适合您的建议取决于您调用对象的顺序和调用的方法。例如,如果您使用double dot method,那么您创建了一个不可关闭的对象。即使您听取所有建议并构建代码以避免出现问题,但当 SSIS 包部署到服务器时,情况会发生变化。微软基本上已经告诉开发者not do this

      Microsoft 目前不推荐也不支持任何无人值守、非交互式客户端应用程序或组件(包括 ASP、ASP.NET、DCOM 和 NT 服务)的 Microsoft Office 应用程序自动化,因为 Office 可能表现出不稳定Office 在此环境中运行时的行为和/或死锁。

      奇怪的事情发生了。此外,它不会扩展。包将共享同一个 Excel 实例。一个包的清理例程可以终止另一个包的运行中操作。

      听着,我知道Interop.Excel 上有很多方法,甚至还有更多关于如何清理它们的方法。但是,这些文章针对的是在自己的机器上运行单个代码实例的个人。不建议将其用于具有 SSIS 的企业环境。是的,这很臭。

      但是,有解决方案! NetOffice 类似于Interop.Excel 并帮助处理关闭和释放 COM 对象。我的强烈偏好是EPPlus API,它非常轻量级,类似于Interop.Excel,并且在没有清理问题的情况下快10倍。除了打印和宏之外,它几乎可以做所有 Interop 可以做的事情。甚至比 C# 更好的是开始使用微软提供的 built-in SSIS Excel components 来完成 ETL 工作。

      祝你好运!

      【讨论】:

        【解决方案3】:

        有两种方法可以实现:

        第一种方法

        您可以创建一个自定义的ReleaseObject 函数,如下所示:

        private void ReleaseObject(object obj)
        {
            try {
                int intRel = 0;
                do {
                    intRel = System.Runtime.InteropServices.Marshal.ReleaseComObject(obj);
                } while (intRel > 0);
        
            } catch (Exception ex) {
        
                obj = null;
            } finally {
                GC.Collect();
            }
        }
        

        并使用以下代码顺序:

        xlWorkBook.SaveAs("....");
        xlWorkBook.Close();
        xlApp.Quit();
        
        ReleaseObject(xlRange);
        ReleaseObject(xlWorkSheet);
        ReleaseObject(xlWorkBook);
        ReleaseObject(xlApp);
        

        第二种方法

        在脚本任务中创建一个类,创建一个获取Excel.Application的进程ID并杀死它的函数

        using Excel = Microsoft.Office.Interop.Excel;
        using System.Runtime.InteropServices;
        using System.Diagnostics;
        
        class Sample
        {
            [DllImport("user32.dll")]
            static extern int GetWindowThreadProcessId(int hWnd, out int lpdwProcessId);
        
            Process GetExcelProcess(Excel.Application excelApp)
            {
                int id;
                GetWindowThreadProcessId(excelApp.Hwnd, out id);
                return Process.GetProcessById(id);
            }
        }
        

        然后你可以使用GetExcelProcess(XlApp).Kill();

        【讨论】:

          【解决方案4】:

          导入;

          using System.Diagnostics;
          

          杀僵尸excel使用此功能;

          private void KillSpecificExcelFileProcess(string excelFileName)
              {
                  var processes = from p in Process.GetProcessesByName("EXCEL")
                                  select p;
          
                  foreach (var process in processes)
                  {
                      if (process.MainWindowTitle == excelFileName)
                          process.Kill();
                  }
              }
          

          并调用如下函数; (Interop excel 是无名的,因为我们应该使用 ("")。这可以解决您的问题。

          KillSpecificExcelFileProcess("");
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2015-06-21
            • 1970-01-01
            • 1970-01-01
            • 2018-12-08
            • 1970-01-01
            • 2015-03-15
            • 2014-01-14
            • 1970-01-01
            相关资源
            最近更新 更多