[英]How to clean up COM references in .NET when app will be left running?
I am working on a .NET program that starts a new instance of Excel, does some work, then ends, but must leave Excel running. 我正在开发一个.NET程序,它启动一个新的Excel实例,做一些工作,然后结束,但必须让Excel运行。 Later, when the program runs again, it will attempt to hook into the previous instance.
稍后,当程序再次运行时,它将尝试挂钩到前一个实例。
What is the best way to handle the releasing of COM objects in this situation? 在这种情况下处理COM对象释放的最佳方法是什么? If I do not do a "ReleaseComObject" on the app object the first time, then on the second run get the active object, then finally release the com object, do I have a memory leak?
如果我第一次没有在app对象上执行“ReleaseComObject”,那么在第二次运行时获取活动对象,然后最终释放com对象,我是否有内存泄漏?
The following simplified code illustrates what I am trying to do: 以下简化代码说明了我要做的事情:
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;
}
Thanks 谢谢
In general, when working with the Office PIAs, i've found that these sorts of problems with objects not being released arise when you have instance variables that hold COM objects. 通常,在使用Office PIA时,我发现当您拥有包含COM对象的实例变量时,会出现这些对象未被释放的问题。 In your case, these would be
xlsApp
and xlsWb
. 在您的情况下,这些将是
xlsApp
和xlsWb
。 You don't have to quit the Excel application in order to release the objects, but you do have to perform the following as a cleanup procedure: 你不必为了释放对象退出Excel应用程序,但你必须执行以下的清理过程:
Marshal.FinalReleaseComObject(xlsWb);
xlsWb = null;
Marshal.FinalReleaseComObject(xlsApp);
xlsApp = null;
GC.Collect();
Local-scoped variables containing COM objects don't seem to cause this problem, only instance variables. 包含COM对象的本地范围变量似乎不会导致此问题,只会导致实例变量。 I hope this helps!
我希望这有帮助!
I'm a tad confused - at the end of RunMeFirst
you exit the process, so is RunMeSecond
run in a different process from the first method or the same process? 我有点困惑 - 在
RunMeFirst
结束时你退出进程, RunMeSecond
在第一个方法或同一个进程的不同进程中运行吗?
Either way I would change your code so that xlsWb
is scoped locally and just do the following: 无论哪种方式,我都会更改您的代码,以便
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);
}
You shouldn't really call any of the ReleaseComObject
methods except in certain circumstances (for example in server applications where many COM objects will all be in use and so it is critical to release objects as soon as possible). 除非在某些情况下(例如,在许多COM对象都将被使用的服务器应用程序中,因此尽快释放对象至关重要),您不应该真正调用任何
ReleaseComObject
方法。 The usual mechanism for cleaning up COM object should simply be to let them go out of scope, in which case the COM object will be released using the same magic the GC uses (I believe that it is cleaned up as the finalizer is run, but I'm not 100% sure on this). 清理COM对象的常用机制应该只是让它们超出范围,在这种情况下,COM对象将使用GC使用的相同魔法释放(我相信它会在终结器运行时被清理,但是我不是百分百肯定的。)
The reason why its a good idea to scope xlsWb
locally (rather than as a class member or a static member) is that class members will only be cleaned up when the class is cleaned up, and static members are never cleaned up until the appdomain is unloaded. 在本地(而不是作为类成员或静态成员)范围内
xlsWb
的好主意的原因是,只有在清理类时才会清除类成员,并且在appdomain为止之前永远不会清除静态成员卸载。 If you do need to clean up a COM object referenced by a static field then the way to do this is to set the static field to null
so the underlying COM object can be cleaned up by the GC scoping rules: 如果确实需要清理静态字段引用的COM对象,那么执行此操作的方法是将静态字段设置为
null
以便GC范围规则可以清除基础COM对象:
xlsWb = null;
Also there should be no real need to call GC.Collect
for similar reasons - Mike Rosenblum gives a fair explanation as to why he's calling GC.Collect
and ReleaseComObject
, but no real justification as to why he isn't just satisfied with letting the garbage collector do its job. 此外,由于类似的原因,不应该真正需要调用
GC.Collect
Rosenblum给出了他为什么调用GC.Collect
和ReleaseComObject
的公平解释,但没有真正的理由为什么他不仅仅满足于放弃垃圾收藏家做它的工作。 Like I said, there are situations where you might want more control over the release of COM objects, however these are the exception not the rule. 就像我说的那样,有些情况下你可能想要更多地控制COM对象的释放,但是这些是例外而不是规则。
You may also find reading Marshal.ReleaseComObject Considered Dangerous useful. 您可能还会发现阅读Marshal.ReleaseComObject Considered Dangerous非常有用。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.