当应用程序继续运行时,如何清理 .NET 中的 COM 引用?
我正在开发一个 .NET 程序,该程序启动 Excel 的新实例,执行一些工作,然后结束,但必须保持 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;
}
谢谢
I am working on a .NET program that starts a new instance of Excel, does some work, then ends, but must leave Excel running. 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? 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?
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
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
一般来说,在使用 Office PIA 时,我发现当您拥有保存 COM 对象的实例变量时,会出现此类对象未释放的问题。在您的情况下,这些将是
xlsApp
和xlsWb
。您不必退出 Excel 应用程序来释放对象,但您必须执行以下操作作为清理过程:包含 COM 对象的本地范围变量似乎不会导致这个问题,只有实例变量。我希望这有帮助!
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. In your case, these would be
xlsApp
andxlsWb
. 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:Local-scoped variables containing COM objects don't seem to cause this problem, only instance variables. I hope this helps!
我有点困惑 - 在 RunMeFirst 结束时退出进程,那么 RunMeSecond 是在与第一个方法不同的进程中运行还是在同一进程中运行?
无论哪种方式,我都会更改您的代码,以便将
xlsWb
范围限定在本地,然后执行以下操作:您不应该真正调用任何
ReleaseComObject
方法,除非在某些情况下(例如例如,在服务器应用程序中,许多 COM 对象都将被使用,因此尽快释放对象至关重要)。清理 COM 对象的常用机制应该只是让它们超出范围,在这种情况下,COM 对象将使用 GC 使用的相同魔法来释放(我相信它在终结器运行时被清理,但是我对此不是 100% 确定)。将
xlsWb
作用于本地(而不是作为类成员或静态成员)是一个好主意的原因是,类成员只有在类被清理时才会被清理,而静态成员则被清理。在卸载应用程序域之前永远不会进行清理。如果您确实需要清理静态字段引用的 COM 对象,则执行此操作的方法是将静态字段设置为null
,以便 GC 范围规则可以清理底层 COM 对象:出于类似的原因,也不应该真正需要调用 GC.Collect - Mike Rosenblum 给出了他为什么调用 GC.Collect 的合理解释,并且
ReleaseComObject
,但没有真正的理由说明为什么他不满足于让垃圾收集器完成其工作。正如我所说,在某些情况下您可能希望对 COM 对象的释放进行更多控制,但这些只是例外而不是规则。您还可以阅读 Marshal。 ReleaseComObject 被认为是危险的 很有用。
I'm a tad confused - at the end of
RunMeFirst
you exit the process, so isRunMeSecond
run in a different process from the first method or the same process?Either way I would change your code so that
xlsWb
is scoped locally and just do the following: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). 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).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. 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 tonull
so the underlying COM object can be cleaned up by the GC scoping rules: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 callingGC.Collect
andReleaseComObject
, but no real justification as to why he isn't just satisfied with letting the garbage collector do its job. 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.You may also find reading Marshal.ReleaseComObject Considered Dangerous useful.