【问题标题】:Excel process still runs in backgroundExcel 进程仍在后台运行
【发布时间】:2023-04-04 08:44:01
【问题描述】:

我正在使用互操作库从一张 excel 文件中读取一些数据。我通过使用Microsoft.Office.Interop.Excel 得到数据就好了,然后读取工作表中的所有数据。

Application ExcelApp = new Excel.Application();
Workbook excelWorkbook = ExcelApp2.Workbooks.Open(excelFileNamePath);
_Worksheet excelWorksheet = excelWorkbook.Sheets[excelSheetName];
Range excelRange = excelWorksheet.UsedRange;
//...for loops for reading data
ExcelApp.Quit();

但在我的应用程序终止后,我尝试编辑我的 excel 文件并在打开时遇到了一些问题。显然,即使我调用了ExcelApp.Quit(),excel 进程也会继续在后台运行。有没有办法正确关闭应用程序使用的 excel 文件?我是 c# 的新手,所以我可能在这里遗漏了一些东西。

编辑:已解决!显然有一个 rule 用于不鼓励使用 2 个点的 com 对象。

Excel.Application ExcelApp2 = new Excel.Application();
Excel.Workbooks excelWorkbooks = ExcelApp2.Workbooks;
Excel.Workbook excelWorkbook = excelWorkbooks.Open(excelFileName);
Excel._Worksheet excelWorksheet = excelWorkbook.Sheets[excelSheetName];
//...
excelWorkbook.Close();       
Marshal.ReleaseComObject(excelWorkbook);
Marshal.ReleaseComObject(excelWorksheet);
Marshal.ReleaseComObject(excelRange);
ExcelApp2.Quit();

【问题讨论】:

标签: c# .net excel


【解决方案1】:

还有另一个类似的问题 - 和答案 (https://stackoverflow.com/a/17367570/3063884),其中的解决方案是避免使用双点符号。相反,为沿途使用的每个对象定义变量,并在每个对象上单独使用Marshal.ReleaseComObject

直接从链接的解决方案中复制:

var workbook = excel.Workbooks.Open(/*params*/)

---> 改为使用 -->

var workbooks = excel.Workbooks;
var workbook = workbooks.Open(/*params*/)

然后,完成后,释放每个 COM 对象:

Marshal.ReleaseComObject(workbook);
Marshal.ReleaseComObject(workbooks);
Marshal.ReleaseComObject(excel);

【讨论】:

  • 我刚刚发现了同样的问题,它奏效了!谢谢。
【解决方案2】:

很久没有回答,但对我有用的是调用 GC.Collect();来自来电者,

即而不是

main()
{
    ...
    doexcelstuff();
    ...
}

void doexcelstuff()
{
    Excel.Application ExApp2 = new Excel.Application();
    Excel.Workbook excelWb = ExApp2 .Workbooks.Open(excelFName);
    Excel._Worksheet excelWorksheet = excelWb.Sheets[excelSName];
    //...
    excelWb.Close();
    ExApp2.Quit();     
    Marshal.ReleaseComObject(excelWb);
    Marshal.ReleaseComObject(excelWorksheet);
    Marshal.ReleaseComObject(ExApp2);
    excelWb = null;
    excelWorksheet= null;
    ExApp2= null;
    GC.Collect();
}

用上面的Excel不死

只是一个很小的变化,从哪里调用 GC

main()
{
    ...
    doexcelstuff();
    GC.Collect();      // <<-- moved the GC to here (the caller)
    ...
}

void doexcelstuff()
{
    Excel.Application ExApp2 = new Excel.Application();
    Excel.Workbook excelWb = ExApp2 .Workbooks.Open(excelFName);
    Excel._Worksheet excelWorksheet = excelWb.Sheets[excelSName];
    //...
    excelWb.Close();
    ExApp2.Quit();     
    Marshal.ReleaseComObject(excelWb);
    Marshal.ReleaseComObject(excelWorksheet);
    Marshal.ReleaseComObject(ExApp2);
    excelWb = null;
    excelWorksheet= null;
    ExApp2= null;
    // removed the GC from here
}

我的猜测是垃圾收集器还需要悄悄地从堆中清理内部创建的临时值(包括引用/指针)——我猜在这种情况下其中一些指向 COM 对象。

(只需稍微了解机器在源代码下的工作原理。)

【讨论】:

    【解决方案3】:

    您没有关闭工作簿。关闭它,然后在退出 Excel 应用程序之前释放它:

    excelWorkbook.close();
    Marshal.ReleaseComObject(excelWorkbook);
    ExcelApp.Quit();
    

    【讨论】:

    • 还是会弹出错误。它说“名称为 ... 的文档已打开”
    【解决方案4】:

    对我来说这很有效!

    //Initializing
    Application excelApp = new Application();
    Workbook excelWorkbook = excelApp.Workbooks.Open(pathExcelFile, 0, true, 5, "", "", 
                                   true, XlPlatform.xlWindows, "\t", false, false, 0, 
                                   true, 1, 0);
    Worksheet excelWorksheet = (Worksheet)excelWorkbook.Sheets[1];
    
    //closing
    excelWorksheet = null;
    
    excelWorkbook.Close();
    excelWorkbook = null;
    
    excelApp.Quit();
    excelApp = null;
    

    【讨论】:

      【解决方案5】:

      Interop 是出了名的错误...我在保存我的工作簿后使用以下方法,并且当我退出我的应用程序时 Excel 保持打开状态不再存在问题:

      while (Marshal.ReleaseComObject(wb) > 0);
      while (Marshal.ReleaseComObject(xl) > 0);
      
      wb = null;
      xl = null;
      
      GC.WaitForPendingFinalizers();
      GC.Collect();
      GC.WaitForPendingFinalizers();
      GC.Collect();
      

      wb 是我的工作簿对象,xl 是我的 Excel.Application 对象。

      【讨论】:

      • 这似乎也不起作用。我可以看到它现在有多bug,这太烦人了!
      • 在调用上面的代码之前,你是否保存并关闭了你的东西?我这样做: wb.SaveAs(_saveFile, Microsoft.Office.Interop.Excel.XlFileFormat.xlWorkbookDefault, Type.Missing, Type.Missing, false, false, Microsoft.Office.Interop.Excel.XlSaveAsAccessMode.xlNoChange, Type.Missing, Type.Missing,Type.Missing,Type.Missing,Type.Missing); wb.Saved = true;
      • FWIW,FinalReleaseComObject 方法释放对 COM 对象的托管引用。调用此方法相当于在循环中调用 ReleaseComObject 方法,直到它返回 0(零),正如您在上面所做的那样。在我的例子中,我不需要在调用GC.Collect() 之前将对象设置为空。我也认为不需要两次调用该方法,并且对GC.WaitForPendingFinalizers() 的调用应该发生在GC.Collect() 的调用之后。
      【解决方案6】:

      对我来说这很有效: //打开

              var excelFile = new Application();
              Workbook workBook1 = excelFile.Workbooks.Open(goldenCopy_path);
              Workbook workBook2 = excelFile.Workbooks.Open(new_path);
      
              Worksheet goldWorkSheet, newWorkSheet;
              goldWorkSheet = workBook1.Worksheets[1];
              newWorkSheet = workBook2.Worksheets[1];
      

      //关闭 //它将从visual studio中释放工作簿

              goldWorkSheet = null;
              newWorkSheet = null;
      
              workBook1.Close();
              workBook2.Close();
              workBook1 = null;
              workBook2 = null;
      
              excelFile.Quit();
              excelFile = null;
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-08-20
        • 2018-01-30
        • 2021-04-20
        • 1970-01-01
        • 2023-03-10
        • 2016-04-10
        • 2021-04-06
        相关资源
        最近更新 更多