我正在开发一个.NET程序,它启动一个新的Excel实例,做一些工作,然后结束,但必须让Excel运行。稍后,当程序再次运行时,它将尝试挂钩到前一个实例。
在这种情况下处理COM对象释放的最佳方法是什么?如果我第一次没有在app对象上执行“ReleaseComObject”,那么在第二次运行时获取活动对象,然后最终释放com对象,我是否有内存泄漏?
以下简化代码说明了我要做的事情:
private Microsoft.Office.Interop.Excel.Application xlsApp;
private Microsoft.Office.Interop.Excel.Workbook xlsWb;
public void RunMeFirst()
{
//Start a new instance
System.Type oSEType = Type.GetTypeFromProgID("Excel.Application");
xlsApp = Activator.CreateInstance(oSEType);
xlsWb = xlsApp.Workbooks.Open("C:\\test1.xls");
//Do some stuff
xlsWb.Close(false);
Cleanup(ref xlsWb);
//Do not quit Excel here
//No Cleanup of xlsApp here? Is this OK?
System.Environment.Exit(0);
}
public void RunMeSecond()
{
//Hook into existing instance
xlsApp = Marshal.GetActiveObject("Excel.Application");
xlsWb = xlsApp.Workbooks.Open("C:\\test2.xls");
//Do some stuff
xlsWb.Close(false);
Cleanup(ref xlsWb);
xlsApp.Quit();
Cleanup(ref xlsApp);
System.Environment.Exit(0);
}
public void Cleanup(ref object theObj)
{
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
Marshal.FinalReleaseComObject(theObj);
theObj = null;
}
由于
答案 0 :(得分:4)
通常,在使用Office PIA时,我发现当您拥有包含COM对象的实例变量时,会出现这些对象未被释放的问题。在您的情况下,这些将是xlsApp
和xlsWb
。您不必退出Excel应用程序以释放对象,但执行必须执行以下操作作为清理过程:
Marshal.FinalReleaseComObject(xlsWb);
xlsWb = null;
Marshal.FinalReleaseComObject(xlsApp);
xlsApp = null;
GC.Collect();
包含COM对象的本地范围变量似乎不会导致此问题,只会导致实例变量。我希望这有帮助!
答案 1 :(得分:2)
我有点困惑 - 在RunMeFirst
结束时退出流程,RunMeSecond
是否在与第一个方法或同一流程不同的流程中运行?
无论哪种方式,我都会更改您的代码,以便xlsWb
在本地作用域,并执行以下操作:
public void RunMeFirst()
{
System.Type oSEType = Type.GetTypeFromProgID("Excel.Application");
xlsApp = Activator.CreateInstance(oSEType);
Workbook xlsWb = xlsApp.Workbooks.Open("C:\\test1.xls");
// Do stuff
xlsWb.Close(false);
System.Environment.Exit(0);
}
除非在某些情况下(例如,在许多COM对象都将被使用的服务器应用程序中,因此尽快释放对象至关重要),您不应该真正调用任何ReleaseComObject
方法。 。清理COM对象的常用机制应该只是让它们超出范围,在这种情况下,COM对象将使用GC使用的相同魔法释放(我相信它会在终结器运行时被清理,但是我不是百分百肯定的。)
在本地(而不是作为类成员或静态成员)范围内xlsWb
的好主意的原因是,只有在清理类时才清除类成员,并且静态成员是在卸载appdomain之前,从不清理。如果确实需要清理静态字段引用的COM对象,那么执行此操作的方法是将静态字段设置为null
,以便GC范围规则可以清除基础COM对象:
xlsWb = null;
同样不应该出于类似的原因打电话给GC.Collect
- Mike Rosenblum给出了一个公平的解释,说明他为什么打电话给GC.Collect
和ReleaseComObject
,但没有真正的理由。为什么他不满足于让垃圾收集器完成它的工作。就像我说的那样,有些情况下你可能想要更多地控制COM对象的释放,但是这些是例外而不是规则。
您可能还会发现阅读Marshal.ReleaseComObject Considered Dangerous很有用。