C# 释放 COM 对象的正确方法?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15728676/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Proper way of releasing COM objects?
提问by Alvin Wong
Sometimes when I end the application and it tries to release some COM objects, I receive a warning in the debugger:
有时,当我结束应用程序并尝试释放一些 COM 对象时,我会在调试器中收到警告:
RaceOnRCWCleanUp
was detected
RaceOnRCWCleanUp
被检测到
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?
如果我编写一个使用 COM 对象的类,我是否需要实现IDisposable
并调用Marshal.FinalReleaseComObject
它们IDisposable.Dispose
以正确释放它们?
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 finalizerbut I wonder if this is correct.
如果Dispose
不是手动调用,我还需要在终结器中释放它们还是 GC 会自动释放它们?现在我调用Dispose(false)
终结器,但我想知道这是否正确。
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?
我使用的 COM 对象也有一个类监听的事件处理程序。显然该事件是在另一个线程上引发的,那么如果在处理类时触发它,我该如何正确处理它?
回答by aguyngueran
Based on my experience using different COM objects (in-process or out-of-process) I would suggest one Marshal.ReleaseComObject
per one COM/ .NET boundary crossing (if for an instance you reference COM object in order to retrieve another COM reference).
根据我使用不同 COM 对象(进程内或进程外)的经验,我建议Marshal.ReleaseComObject
每个 COM/.NET 边界跨越一个(如果对于一个实例,您引用 COM 对象以检索另一个 COM 引用)。
I have run into many issues just because I decided to postpone COM interop cleanup to GC.
Also please notice, I never use Marshal.FinalReleaseComObject
- some COM objects are singletons and it doesn't work well with such objects.
我遇到了很多问题,因为我决定将 COM 互操作清理推迟到 GC。另请注意,我从不使用Marshal.FinalReleaseComObject
- 某些 COM 对象是单例对象,并且不适用于此类对象。
Doing anything in managed objects inside finalizer (or Dispose(false) from the well-known IDisposable
implementation) is forbidden. You must not rely on any .NET object reference in the finalizer. You can release IntPtr
, but not COM object as it could already be clean up.
IDisposable
禁止在终结器内的托管对象(或知名实现中的Dispose(false))中执行任何操作。您不得依赖终结器中的任何 .NET 对象引用。您可以释放IntPtr
,但不能释放COM 对象,因为它可能已经被清理。
回答by James Wilkins
There's an article here on that: http://www.codeproject.com/Tips/235230/Proper-Way-of-Releasing-COM-Objects-in-NET
这里有一篇文章:http: //www.codeproject.com/Tips/235230/Proper-Way-of-Releasing-COM-Objects-in-NET
In a nutshell:
简而言之:
1) Declare & instantiate COM objects at the last moment possible. 2) ReleaseComObject(obj) for ALL objects, at the soonest moment possible. 3) Always ReleaseComObject in the opposite order of creation. 4) NEVER call GC.Collect() except when required for debugging.
Until GC naturally occurs, the com reference will not be fully released. This is why so many people need to force object destruction using FinalReleaseComObject() and GC.Collect(). Both are required for dirty Interop code.
1) Declare & instantiate COM objects at the last moment possible. 2) ReleaseComObject(obj) for ALL objects, at the soonest moment possible. 3) Always ReleaseComObject in the opposite order of creation. 4) NEVER call GC.Collect() except when required for debugging.
在 GC 自然发生之前,com 引用不会完全释放。这就是为什么这么多人需要使用 FinalReleaseComObject() 和 GC.Collect() 强制销毁对象的原因。脏互操作代码都需要两者。
Dispose is NOT automatically called by the GC. When an object is being disposed, the destructor is called (in a different thread). This is usually where you could release any unmanaged memory, or com references.
GC 不会自动调用 Dispose。当一个对象被释放时,析构函数被调用(在不同的线程中)。这通常是您可以释放任何非托管内存或 com 引用的地方。
Destructors: http://msdn.microsoft.com/en-us/library/66x5fx1b.aspx
析构函数:http: //msdn.microsoft.com/en-us/library/66x5fx1b.aspx
... when your application encapsulates unmanaged resources such as windows, files, and network connections, you should use destructors to free those resources. When the object is eligible for destruction, the garbage collector runs the Finalize method of the object.
...当您的应用程序封装非托管资源(如窗口、文件和网络连接)时,您应该使用析构函数来释放这些资源。当对象符合销毁条件时,垃圾收集器运行对象的 Finalize 方法。
回答by Govert
First - you neverhave 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.
首先 - 您永远不必打电话Marshal.ReleaseComObject(...)
或Marshal.FinalReleaseComObject(...)
在执行 Excel 互操作时。这是一个令人困惑的反模式,但是任何关于此的信息(包括来自 Microsoft 的信息)表明您必须从 .NET 手动释放 COM 引用都是不正确的。事实是 .NET 运行时和垃圾收集器正确地跟踪和清理 COM 引用。
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.
其次,如果要确保在进程结束时清除对进程外 COM 对象的 COM 引用(以便 Excel 进程将关闭),则需要确保垃圾收集器运行。您可以通过调用GC.Collect()
和正确执行此操作GC.WaitForPendingFinalizers()
。调用两次是安全的, end 确保循环也被清理干净。
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.
第三,在调试器下运行时,局部引用将人为地保持活动状态直到方法结束(以便局部变量检查工作)。因此,GC.Collect()
调用对于rng.Cells
从同一方法中清除对象无效。您应该将执行 COM 互操作的代码从 GC 清理拆分为单独的方法。
The general pattern would be:
一般模式是:
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
There is a lot of false information and confusion about this issue, including many posts on MSDN and on StackOverflow.
关于这个问题有很多虚假信息和混淆,包括 MSDN 和 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.
最终说服我仔细研究并找出正确建议的是这篇文章https://blogs.msdn.microsoft.com/visualstudio/2010/03/01/marshal-releasecomobject-thinked-dangerous/以及找到在某些 StackOverflow 答案上的调试器下引用保持活动的问题。