【问题标题】:Background Excel Thread Not Getting Killed C#后台 Excel 线程没有被杀死 C#
【发布时间】:2016-09-05 13:44:24
【问题描述】:

需要使用 Excel 互操作。我可以成功打开并从 excel 文件中读取,但是在关闭它时,该 excel 的后台进程不会被杀死。尝试使用以前 SO 链接中的几种解决方案,但没有运气!所以我的问题是,如何杀死后台进程???

以下是我目前使用的更新代码

Excel.Application application = new Excel.Application();

var workbooks = application.Workbooks;
Excel.Workbook workbook = workbooks.Open(path);
Excel.Worksheet worksheet = workbook.ActiveSheet;
Excel.Range range = worksheet.UsedRange;
var rows = range.Rows;

// Some business logic  

for (int row = 2; row <= rows.Count; row++)
{
   //Read the data from the excel
}

// Some business logic

//close the excel
rows.Clear();
cell.Clear();
range.Clear();

workbook.Close(false);
application.Quit();

while (Marshal.FinalReleaseComObject(rows) != 0) { }
while (Marshal.FinalReleaseComObject(cell) != 0) { }
while (Marshal.FinalReleaseComObject(range) != 0) { }
while (Marshal.FinalReleaseComObject(worksheet) != 0) { }
while (Marshal.FinalReleaseComObject(workbook) != 0) { }
while (Marshal.FinalReleaseComObject(workbooks) != 0) { }
while (Marshal.FinalReleaseComObject(application) != 0) { }

rows = null;
cell = null;
range = null;
worksheet = null;
workbook = null;
workbooks = null;
application = null;
GC.Collect();
GC.WaitForPendingFinalizers();

按照上面的代码,我在调试器中得到以下异常

我们将不胜感激。

【问题讨论】:

  • 你没有释放range。此外,如果您通过Marshal.ReleaseComObjectGC.CollectGC.WaitForPendingFinalizers 手动释放 RCW,则不需要。最后,使用Marshal.FinalReleaseComObject,它会在一次通话中为您完成循环。
  • 另外,作为一种好的做法,倾向于将此类代码包装在 try-finally 块中,以确保您在正常执行和可恢复的错误情况下正确释放所有 COM 对象。
  • @inBetween:是的,代码被包装在一个 try-catch 中,在我在这里发布之前和之后还有一些东西,认为它们不是必需的,因为它们只是用于其他一些 BL 实现:)
  • @inBetween 尝试了所有这些,但没有好的结果:( 我真的很傻,错过了一些非常小的东西??感觉很愚蠢!!:/
  • 这可能根本不是,但以防万一;当我使用 Excel 互操作时,我倾向于从最深到最浅释放 COM 对象。即从范围 -> 工作表 -> 工作簿 -> 工作簿 -> .... -> 应用程序开始反转发布顺序。还可以考虑显示更多代码,也许你持有一些你没有注意到的 COM 对象的引用。

标签: c# .net asp.net-mvc excel background-process


【解决方案1】:

您正在使用 range.Rows.Count,这可能违反“永远不要在 com 对象中使用 2 个点”。规则。 See here

你可以尝试加入这个;

var rows = range.Rows
for (int row = 2; row <= rows.Count; row++)
{
   //Read the data from the excel
}
rows.Clear(); //rows is itself a range object 

【讨论】:

  • 试过了。但没有运气 :( 感谢您的输入 :)
  • 你试过了吗? Marshal.ReleaseComObject(range), Marshal.ReleaseComObject(rows)
  • 是的,这样做了,但仍然无法正常工作。甚至尝试了 FinalReleaseComObject() 而不是 ReleaseComObject()。但仍然没有运气:(
【解决方案2】:

我不得不使用 Excel 互操作的几次,遵循这些简单的规则时我没有遇到任何问题:

  1. 始终将任何 Excel 互操作包装在 try-finally 中 块。在finally 块中放置所有释放逻辑。
  2. 使用Marshal.FinalReleaseComObject 释放命名COM 引用,因为它本质上是为您执行引用计数循环。
  3. 从最深处急切释放 COM 对象到 最浅的。在你的情况下,我会从 range 开始,然后是 worksheet 然后workbook 等等。
  4. 使用GC.Collect()GC.WaitForPendingFinalizers() 正确释放未引用的COM 对象(两点规则)。手动执行此操作之前 释放您持有命名引用的待处理 COM 对象。

【讨论】:

  • @IamRuku 这根本不是运行时异常。似乎正在发生的事情是,您只是试图在 application 发布后对其进行检查。 RCW 引用计数已达到零,并且运行时已释放其对 COM 对象 (application) 的所有引用。
  • 如果是这样的话,那后台进程怎么还在任务管理器中持续存在呢?因为,我可以看到所有其他资源都被清理(使用 VS),并且没有抛出其他异常!
  • @IamRuku 我不知道,因为我看不到您的所有代码。某处某处使对某些 COMObject 的引用保持活动状态。您正确地将对application 的引用计数设置为零这一事实并不意味着如果其他东西仍在直接或间接地引用它,则该对象已被释放。你不能再使用application 引用了。
  • 在我刚刚上传到 GitHub 的项目中查看这个小 class。这是我通常处理 Excel 互操作的方式,我从来没有遇到过任何问题。顺便说一句,我正在使用 Excel 版本 2007 - 2013。
猜你喜欢
  • 2018-03-08
  • 2011-04-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-15
相关资源
最近更新 更多