.net 在我的析构函数中释放 Excel 对象
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2085972/
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
Release Excel Object In My Destructor
提问by Murat
I'm writing a Excel class using Microsoft.Interropt.Excel DLL. I finish all function but I have an error in my Destructor. I Want to save all changes to my file and I want to release all source. I want to all of them in my destructor. But In my destructor, Excel.ApplicationClass, Workbook and Worksheet objects are fill by an Exception which have message "COM object that has been separated from its underlying RCW cannot be used." So I can't save nothing, close nothing because ? can't access workbook or worksheet object.
我正在使用 Microsoft.Interropt.Excel DLL 编写 Excel 类。我完成了所有功能,但我的析构函数中有错误。我想保存对我的文件的所有更改,我想发布所有源代码。我想把它们都放在我的析构函数中。但是在我的析构函数中,Excel.ApplicationClass、Workbook 和 Worksheet 对象由一个异常填充,其中包含消息“无法使用已与其底层 RCW 分离的 COM 对象”。所以我什么都不能保存,什么都不关闭,因为?无法访问工作簿或工作表对象。
Can't I access the class private members in Destructor?
我不能访问析构函数中的类私有成员吗?
回答by Mike Rosenblum
The closest thing that .NET has to a destructor, is what .NET calls a finalizer. The main difference is that a destructor typically has deterministic finalization (say, when the reference count on the object becomes zero), while a .NET finalizer is called at an undeterminedtime after the object is no longer referenced. This is handled by the .NET Garbage Collector using a roots-tracing procedure, rather than using simple reference counting.
.NET 最接近析构函数的是 .NET 称为终结器的东西。主要区别在于析构函数通常具有确定性的终结(例如,当对象上的引用计数变为零时),而 .NET 终结器在对象不再被引用后的不确定时间被调用。这是由 .NET 垃圾收集器使用根跟踪过程处理的,而不是使用简单的引用计数。
One of the best articles on this is Garbage Collection: Automatic Memory Management in the Microsoft .NET Framework. For more on finalizers specifically, see the article Finalize Methods and Destructorsin MSDN.
最好的文章之一是垃圾收集:Microsoft .NET Framework 中的自动内存管理。有关终结器的详细信息,请参阅MSDN 中的文章终结方法和析构函数。
Can't I access the class private members in Destructor?
我不能访问析构函数中的类私有成员吗?
No, you cannot do so safely.
不,你不能安全地这样做。
What is happening in your case is that when your object is no longer directly or indirectly referenced by a root, the COM objects that your object references -- that is, the objects referenced by your private fields -- are also not referenced by a root either. (Being reference by your object's fields does notkeep these COM objects alive because your object is no longer referenced by, or traced from, a root, and, therefore, the COM objects do not trace from a root either.) So your object and all the COM objects it references are all ready to be garbage collected at the same time. Some time later, the garbage collector will cleanup your object and call its finalizer, as it will also do with the COM objects, each of which is really a Runtime Callable Wrapper (RCW).
在您的情况下发生的情况是,当您的对象不再被根直接或间接引用时,您的对象引用的 COM 对象(即私有字段引用的对象)也不会被根引用任何一个。(被您的对象的字段引用并不会使这些 COM 对象保持活动状态,因为您的对象不再被根引用或跟踪,因此,COM 对象也不会从根跟踪。)所以您的对象和所有的COM对象引用它都愿意被垃圾收集在同一时间。一段时间后,垃圾收集器将清理您的对象并调用它的终结器,就像它也会处理 COM 对象一样,每个对象实际上都是一个运行时可调用包装器 (RCW).
The trouble is that not only is the timing as to when these objects are to be garbage collected uncertain, but the orderin which the finalizers are called is also nondeterministic. In this case, a Runtime Callable Wrapper also has a finalizer, which calls Marshal.ReleaseComObjecton itself, which has the result of decrementing the reference count on the COM side of the fence so that this COM object can be released. But since the order in which the finalizers are called is uncertain, it is very possible that the finalizers for the COM objects that your object references will fire beforethe finalizer for your object. So the code within your finalizer couldwork sometimes, but, most of the time, one or more of the Runtime Callable Wrappers that your object references will have already had their finalizers called, and the underlying COM object will have been released, before your finalizer gets to execute its code.
问题在于,不仅这些对象何时被垃圾回收的时间不确定,而且调用终结器的顺序也是不确定的。在这种情况下,Runtime Callable Wrapper 也有一个终结器,它对自身调用Marshal.ReleaseComObject,其结果是在栅栏的 COM 端递减引用计数,以便可以释放此 COM 对象。但是由于调用终结器的顺序是不确定的,对象引用的 COM 对象的终结器很可能会在对象的终结器之前触发。所以你的终结器中的代码可以有时会起作用,但大多数情况下,对象引用的一个或多个运行时可调用包装器已经调用了它们的终结器,并且在终结器执行其代码之前,底层 COM 对象将被释放。
In short, you should avoid using finalizers in general, and you should never access reference types from within a finalizer, because these reference types may have already been finalized.
简而言之,您应该避免使用终结器,并且永远不要从终结器中访问引用类型,因为这些引用类型可能已经被终结了。
To remedy your situation, I would consider two different possibilities:
为了解决您的情况,我会考虑两种不同的可能性:
Dispose of the COM objects within the same method that creates them. I have a couple of discussions on this hereand here.
Enable deterministic disposal of your object by making use of the IDisposable interface, instead of relying on the non-deterministic finalizer.
通过使用IDisposable 接口启用对象的确定性处置,而不是依赖于非确定性终结器。
For articles on how to implement the IDisposable pattern, see:
有关如何实现 IDisposable 模式的文章,请参阅:
- Implementing a Dispose Method (MSDN)
- Dispose, Finalization, and Resource Management (Joe Duffy)
- Dispose and Finalize methods (Rohit Gupta)
-- Mike
——迈克
回答by sdmcnitt
I am not sure if I am coding something wrong--tried to follow the examples here. I found that when I take advantage of the IDisposable pattern everything works unless I need to handle workbook events.
我不确定我是否在编码错误 - 尝试按照此处的示例进行操作。我发现当我利用 IDisposable 模式时,除非我需要处理工作簿事件,否则一切正常。
In my scenario the user can close the Workbook before they close the app. I have declared Excel object WithEvents and coded the WorkbookBeforeClose handler to meet the requirements.
在我的场景中,用户可以在关闭应用程序之前关闭工作簿。我已经声明了 Excel 对象 WithEvents 并编码了 WorkbookBeforeClose 处理程序以满足要求。
In this scenario I get the "COM object that has been separated from its underlying RCW cannot be used" error when I go to close my app (and I have already closed the Excel). The error is happening in the Finalize when it calls Dispose(False).
在这种情况下,当我关闭我的应用程序(并且我已经关闭 Excel)时,我收到“无法使用与其底层 RCW 分离的 COM 对象”错误。当它调用 Dispose(False) 时,Finalize 中发生了错误。
The problem goes away if I leave the Excel object declared with events but do not code any handlers.
如果我保留使用事件声明的 Excel 对象但不编写任何处理程序,问题就会消失。
In my Dispose I had to swallow the error for my Workbooks.Close and Quit as they were the statements that caused the error.
在我的 Dispose 中,我不得不吞下 Workbooks.Close 和 Quit 的错误,因为它们是导致错误的语句。
回答by Joe
No, you should not be accessing any managed objects in a destructor: this includes COM RCWs.
不,您不应该访问析构函数中的任何托管对象:这包括 COM RCW。
Instead, implement the standard IDisposable pattern, and release your COM objects in the Dispose(bool) method like you would a disposable managed object.
相反,实现标准的 IDisposable 模式,并像处理一次性托管对象一样在 Dispose(bool) 方法中释放 COM 对象。

