我在C#(ApplicationClass)中使用Excel互操作,并在finally子句中放置了以下代码:
while (System.Runtime.InteropServices.Marshal.ReleaseComObject(excelSheet) != 0) { }
excelSheet = null;
GC.Collect();
GC.WaitForPendingFinalizers();
尽管这种方法有效,但即使在我关闭Excel之后,Excel.exe进程仍处于后台。它只在我的应用程序被手动关闭后发布。
我做错了什么,或者是否有其他方法可以确保正确处理互操作对象?
尝试之后
按相反顺序释放COM对象在末尾添加两次GC.Collect()和GC.WaitForPendingFinalizers()不超过两个点关闭工作簿并退出应用程序以释放模式运行
对我有效的最终解决方案是移动一组
GC.Collect();
GC.WaitForPendingFinalizers();
我们将其添加到包装器的函数末尾,如下所示:
private void FunctionWrapper(string sourcePath, string targetPath)
{
try
{
FunctionThatCallsExcel(sourcePath, targetPath);
}
finally
{
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
我的回答很晚,其唯一目的是支持戈弗特提出的解决方案。
简短版本:
编写一个没有全局变量和参数的局部函数执行COM内容。在调用COM的包装函数中调用COM函数功能,然后进行清洁。
长版本:
您没有使用.Net来计算COM对象的引用数,并以正确的顺序自行释放它们。即使C++程序员也不再使用智能指针来实现这一点。所以,忘掉Marshal.ReleaseComObject和有趣的一点好两点坏规则吧。如果您对不再需要的COM对象的所有引用都为空,GC很乐意做释放COM对象的工作。最简单的方法是在一个局部函数中处理COM对象,COM对象的所有变量在最后自然地超出了范围。由于Hans Passant的精彩回答中指出了调试器的一些奇怪特性,在Post-Mortem的公认答案中提到,清理应该委托给一个包装函数,该包装函数也调用执行函数。因此,像Excel或Word这样的COM对象需要两个函数,一个执行实际任务,一个包装器调用此函数,然后像Govert那样调用GC,这是本线程中唯一正确的答案。为了说明这个原理,我使用了一个适合所有做COM的函数的包装器。除了这个扩展,我的代码只是Govert代码的C#版本。此外,我停止了该过程6秒,以便您可以在任务管理器中检查Excel在Quit()之后不再可见,而是一直保持僵尸状态,直到GC结束它。
using Excel = Microsoft.Office.Interop.Excel;
public delegate void WrapCom();
namespace GCTestOnOffice{
class Program{
static void DoSomethingWithExcel(){
Excel.Application ExcelApp = new();
Excel.Workbook Wb = ExcelApp.Workbooks.Open(@"D:\\Sample.xlsx");
Excel.Worksheet NewWs = Wb.Worksheets.Add();
for (int i = 1; i < 10; i++){ NewWs.Cells[i, 1] = i;}
Wb.Save();
ExcelApp.Quit();
}
static void TheComWrapper(WrapCom wrapCom){
wrapCom();
//All COM objects are out of scope, ready for the GC to gobble
//Excel is no longer visible, but the process is still alive,
//check out the Task-Manager in the next 6 seconds
Thread.Sleep(6000);
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
//Check out the Task-Manager, the Excel process is gone
}
static void Main(string[] args){
TheComWrapper(DoSomethingWithExcel);
}
}
}