如何在.NET中清除COM引用时应用程序将保持运行?

我正在开发一个启动Excel的新实例的.NET程序,做了一些工作,然后结束,但必须让Excel运行。 稍后,当程序再次运行时,它将尝试挂钩到前一个实例。

在这种情况下处理COM对象释放的最好方法是什么? 如果我第一次没有在应用程序对象上执行“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; } 

谢谢

一般来说,在使用Office PIA时,我发现当有实例variables存放COM对象时,会出现这些对象没有被释放的问题。 在你的情况下,这些将是xlsAppxlsWb 。 您不必为了释放对象而退出Excel应用程序,但是您必须执行以下操作作为清理过程:

 Marshal.FinalReleaseComObject(xlsWb); xlsWb = null; Marshal.FinalReleaseComObject(xlsApp); xlsApp = null; GC.Collect(); 

包含COM对象的局部范围variables似乎不会导致这个问题,只有实例variables。 我希望这有帮助!

我有点困惑 – 在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使用的相同的魔法来释放(我相信它是在终结器运行时被清理的,但是我不是100%肯定的)。

为什么在本地范围内使用xlsWb (而不是类成员或静态成员)是一个好主意,因为类成员只能在清理类时清理干净,静态成员从不清理,直到应用程序域卸载。 如果您确实需要清理由静态字段引用的COM对象,那么执行此操作的方法是将静态字段设置为null以便通过GC作用域规则可以清除基础COM对象:

 xlsWb = null; 

也不应该有真正需要调用GC.Collect类似的原因 – 迈克罗森布卢姆给出了一个公平的解释,为什么他叫GC.CollectReleaseComObject ,但没有真正的理由,为什么他不满足于让垃圾collections家的工作。 就像我所说的,在某些情况下,你可能需要更多地控制COM对象的释放,但是这些都不是规则的例外。

您也可能会发现阅读Marshal.ReleaseComObject认为危险有用。