问题描述
有时当我结束应用程序并尝试释放一些 COM 对象时,我会在调试器中收到警告:
Sometimes when I end the application and it tries to release some COM objects, I receive a warning in the debugger:
RaceOnRCWCleanUp
被检测到
如果我编写一个使用 COM 对象的类,我是否需要实现 IDisposable
并在 IDisposable.Dispose
Marshal.FinalReleaseComObject> 正确释放它们?
If I write a class which uses COM objects, do I need to implement IDisposable
and call Marshal.FinalReleaseComObject
on them in IDisposable.Dispose
to properly release them?
如果 Dispose
没有手动调用,我还需要在终结器中释放它们还是 GC 会自动释放它们?现在我在终结器中调用Dispose(false)
,但我想知道这是否正确.
If Dispose
is not called manually then, do I still need to release them in the finalizer or will the GC release them automatically? Now I call Dispose(false)
in the finalizer but I wonder if this is correct.
我使用的 COM 对象也有一个事件处理程序,该类可以监听.显然该事件是在另一个线程上引发的,那么如果在处置类时触发它,我该如何正确处理它?</p>
The COM object I use also have an event handler which the class listens to. Apparently the event is raised on another thread, so how do I correctly handle it if it is fired when disposing the class?
推荐答案
首先 - 你永远不必调用 Marshal.ReleaseComObject(...)
或 Marshal.FinalReleaseComObject(...)
在进行 Excel 互操作时.这是一个令人困惑的反模式,但任何有关此的信息(包括来自 Microsoft 的)表明您必须从 .NET 手动释放 COM 引用都是不正确的.事实上,.NET 运行时和垃圾收集器正确地跟踪和清理 COM 引用.
First - you never have to call Marshal.ReleaseComObject(...)
or Marshal.FinalReleaseComObject(...)
when doing Excel interop. It is a confusing anti-pattern, but any information about this, including from Microsoft, that indicates you have to manually release COM references from .NET is incorrect. The fact is that the .NET runtime and garbage collector correctly keep track of and clean up COM references.
其次,如果要确保在进程结束时清理对进程外 COM 对象的 COM 引用(以便 Excel 进程将关闭),则需要确保垃圾收集器运行.您可以通过调用 GC.Collect()
和 GC.WaitForPendingFinalizers()
正确地做到这一点.调用两次是安全的,end 确保循环也被清理干净.
Second, if you want to ensure that the COM references to an out-of-process COM object is cleaned up when your process ends (so that the Excel process will close), you need to ensure that the Garbage Collector runs. You do this correctly with calls to GC.Collect()
and GC.WaitForPendingFinalizers()
. Calling twice is safe, end ensures that cycles are definitely cleaned up too.
第三,在调试器下运行时,局部引用会人为地保持活动状态,直到方法结束(这样局部变量检查才起作用).因此,GC.Collect()
调用对于从同一方法中清除像 rng.Cells
这样的对象无效.您应该将执行 COM 互操作的代码从 GC 清理中拆分为单独的方法.
Third, when running under the debugger, local references will be artificially kept alive until the end of the method (so that local variable inspection works). So a GC.Collect()
calls are not effective for cleaning object like rng.Cells
from the same method. You should split the code doing the COM interop from the GC cleanup into separate methods.
一般模式是:
Sub WrapperThatCleansUp()
' NOTE: Don't call Excel objects in here...
' Debugger would keep alive until end, preventing GC cleanup
' Call a separate function that talks to Excel
DoTheWork()
' Now Let the GC clean up (twice, to clean up cycles too)
GC.Collect()
GC.WaitForPendingFinalizers()
GC.Collect()
GC.WaitForPendingFinalizers()
End Sub
Sub DoTheWork()
Dim app As New Microsoft.Office.Interop.Excel.Application
Dim book As Microsoft.Office.Interop.Excel.Workbook = app.Workbooks.Add()
Dim worksheet As Microsoft.Office.Interop.Excel.Worksheet = book.Worksheets("Sheet1")
app.Visible = True
For i As Integer = 1 To 10
worksheet.Cells.Range("A" & i).Value = "Hello"
Next
book.Save()
book.Close()
app.Quit()
' NOTE: No calls the Marshal.ReleaseComObject() are ever needed
End Sub
关于这个问题有很多虚假信息和混淆,包括 MSDN 和 StackOverflow 上的许多帖子.
There is a lot of false information and confusion about this issue, including many posts on MSDN and on StackOverflow.
最终说服我仔细研究并找出正确建议的是这篇文章 https://blogs.msdn.microsoft.com/visualstudio/2010/03/01/marshal-releasecomobject-considered-dangerous/ 以及通过参考找到问题在某些 StackOverflow 答案的调试器下保持活动状态.
What finally convinced me to have a closer look and figure out the right advice was this post https://blogs.msdn.microsoft.com/visualstudio/2010/03/01/marshal-releasecomobject-considered-dangerous/ together with finding the issue with references kept alive under the debugger on some StackOverflow answer.
这篇关于释放 COM 对象的正确方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!