4 回答
TA贡献1834条经验 获得超8个赞
Excel不会退出,因为您的应用程序仍然保持对COM对象的引用。
我猜你是在调用COM对象的至少一个成员而不将其分配给变量。
对我来说,我直接使用excelApp.Worksheets对象而不将其分配给变量:
Worksheet sheet = excelApp.Worksheets.Open(...);...Marshal.ReleaseComObject(sheet);
我不知道内部C#为Worksheets COM对象创建了一个包装器,它没有被我的代码释放(因为我不知道它),这也是为什么没有卸载Excel的原因。
我在这个页面找到了我的问题的解决方案,它也有一个很好的规则,用于在C#中使用COM对象:
切勿对COM对象使用两个点。
因此,有了这些知识,正确的做法是:
Worksheets sheets = excelApp.Worksheets; // <-- The important partWorksheet sheet = sheets.Open(...);... Marshal.ReleaseComObject(sheets);Marshal.ReleaseComObject(sheet);
POST MORTEM更新:
我希望每个读者都能仔细阅读Hans Passant的这个答案,因为它解释了我和许多其他开发人员陷入困境的陷阱。当我几年前写这个答案时,我不知道调试器对垃圾收集器的影响,并得出了错误的结论。为了历史,我保持我的答案不变,但请阅读此链接,不要采用“两点”的方式:了解.NET中的垃圾收集和使用IDisposable清理Excel互操作对象
TA贡献1827条经验 获得超8个赞
您实际上可以干净地释放Excel应用程序对象,但您必须小心。
维护一个命名引用的建议绝对是你访问的每个COM对象,然后通过它明确地发布它Marshal.FinalReleaseComObject()在理论上是正确的,但不幸的是,在实践中很难管理。如果有人在任何地方滑动并使用“两个点”,或者通过for each循环或任何其他类似命令迭代单元格,那么你将拥有未引用的COM对象并冒着挂起的风险。在这种情况下,无法在代码中找到原因; 你必须通过眼睛审查你的所有代码,并希望找到原因,这个任务对于大型项目来说几乎是不可能的。
好消息是,您实际上不必维护对您使用的每个COM对象的命名变量引用。相反,调用GC.Collect()然后GC.WaitForPendingFinalizers()释放您没有引用的所有(通常是次要的)对象,然后显式释放您持有命名变量引用的对象。
您还应该按重要性的相反顺序发布命名引用:首先是范围对象,然后是工作表,工作簿,最后是Excel应用程序对象。
例如,假设您有一个名为Range的对象变量xlRng,一个名为Worksheet的变量xlSheet,一个名为的Workbook变量xlBook和一个名为的Excel Application变量xlApp,那么您的清理代码可能如下所示:
// Cleanup
GC.Collect();
GC.WaitForPendingFinalizers();
Marshal.FinalReleaseComObject(xlRng);
Marshal.FinalReleaseComObject(xlSheet);
xlBook.Close(Type.Missing, Type.Missing, Type.Missing);
Marshal.FinalReleaseComObject(xlBook);
xlApp.Quit();
Marshal.FinalReleaseComObject(xlApp);
在大多数代码示例中,您将看到从.NET清理COM对象,GC.Collect()并且GC.WaitForPendingFinalizers()调用TWICE,如下所示:
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
但是,除非您使用的是Visual Studio Tools for Office(VSTO),否则不需要这样做,因为它使用终结器来导致在终结队列中提升整个对象图。在下一次垃圾收集之前,这些对象不会被释放。但是,如果你不使用VSTO,你应该能够调用GC.Collect()和GC.WaitForPendingFinalizers()一次。
我知道明确地说GC.Collect()是一个禁忌(当然这两次听起来很痛苦),但说实话,没有办法解决它。通过正常操作,您将生成隐藏的对象,您不会引用该对象,因此您无法通过除调用之外的任何其他方式释放GC.Collect()。
这是一个复杂的话题,但这就是它的全部内容。为清理过程建立此模板后,您可以正常编码,无需包装等。:-)
我在这里有一个教程:
使用VB.Net / COM Interop自动化Office程序
它是为VB.NET编写的,但不要因此而被推迟,其原理与使用C#时的原理完全相同。
- 4 回答
- 0 关注
- 726 浏览
添加回答
举报