在我的析构函数中释放 Excel 对象
我正在使用 Microsoft.Interropt.Excel DLL 编写 Excel 类。 我完成了所有功能,但我的析构函数中有一个错误。 我想保存对我的文件的所有更改,并且我想发布所有源代码。我想把它们全部放在我的析构函数中。 但在我的析构函数中,Excel.ApplicationClass、Workbook 和 Worksheet 对象被异常填充,其中包含消息“无法使用已与其底层 RCW 分离的 COM 对象”。 所以我无法保存任何内容,关闭任何内容,因为我无法访问工作簿或工作表对象。
我无法在析构函数中访问类私有成员吗?
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.
Can't I access the class private members in Destructor?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
.NET 中与析构函数最接近的东西是 .NET 所谓的终结器。主要区别在于,析构函数通常具有确定性终结(例如,当对象的引用计数变为零时),而 .NET 终结器在对象不再被引用后的不确定时间调用。这是由 .NET 垃圾收集器使用根跟踪过程来处理的,而不是使用简单的引用计数。
关于此的最好的文章之一是 垃圾收集:Microsoft .NET Framework 中的自动内存管理。有关终结器的详细信息,请参阅文章 Finalize MSDN 中的方法和析构函数。
不,你不能安全地这样做。
在您的情况下发生的情况是,当您的对象不再直接或间接被根引用时,您的对象引用的 COM 对象(即私有字段引用的对象)也不会被根引用任何一个。 (被对象的字段引用不会使这些 COM 对象保持活动状态,因为您的对象不再由根引用或跟踪,因此,COM 对象不会从根跟踪)因此,您的对象及其引用的所有 COM 对象都已准备好同时进行垃圾收集。一段时间后,垃圾收集器将清理您的对象并调用其终结器,就像它也会处理 COM 对象一样,每个对象实际上都是一个 运行时可调用包装器 (RCW)。
问题在于,不仅这些对象被垃圾收集的时间不确定,而且调用终结器的顺序也是不确定的。在这种情况下,运行时可调用包装器还有一个终结器,它调用 Marshal.ReleaseComObject 本身,其结果是减少栅栏 COM 端的引用计数,以便可以释放该 COM 对象。但由于调用终结器的顺序是不确定的,因此对象引用的 COM 对象的终结器很可能会在对象的终结器之前触发。因此,终结器中的代码有时可以工作,但大多数情况下,对象引用的一个或多个运行时可调用包装器已经调用了它们的终结器,并且底层 COM 对象将在您的终结器执行其代码之前已被释放。
简而言之,您通常应该避免使用终结器,并且永远不应该从终结器内访问引用类型,因为这些引用类型可能已经被终结。
为了解决您的情况,我会考虑两种不同的可能性:
在创建 COM 对象的同一方法中处理它们。我对此此处和此处。
通过使用 IDisposable 接口,而不是依赖于非确定性终结器。
有关如何实现 IDisposable 模式的文章,请参阅:
(罗希特·古普塔)
——迈克
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 undetermined time 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.
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 Destructors in MSDN.
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 not keep 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).
The trouble is that not only is the timing as to when these objects are to be garbage collected uncertain, but the order in which the finalizers are called is also nondeterministic. In this case, a Runtime Callable Wrapper also has a finalizer, which calls Marshal.ReleaseComObject on 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 before the finalizer for your object. So the code within your finalizer could work 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.
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 here and here.
Enable deterministic disposal of your object by making use of the IDisposable interface, instead of relying on the non-deterministic finalizer.
For articles on how to implement the IDisposable pattern, see:
(Rohit Gupta)
-- Mike
我不确定我是否编码错误 - 尝试遵循此处的示例。我发现当我利用 IDisposable 模式时,除非我需要处理工作簿事件,否则一切都会正常。
在我的场景中,用户可以在关闭应用程序之前关闭工作簿。我已经声明了 Excel 对象 WithEvents 并编写了 WorkbookBeforeClose 处理程序以满足要求。
在这种情况下,当我关闭应用程序时(并且我已经关闭了 Excel),我收到“无法使用已与其底层 RCW 分离的 COM 对象”错误。当 Finalize 调用 Dispose(False) 时,会发生错误。
如果我保留使用事件声明的 Excel 对象但不编写任何处理程序,则问题就会消失。
在我的 Dispose 中,我不得不接受 Workbooks.Close 和 Quit 的错误,因为它们是导致错误的语句。
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.
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.
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).
The problem goes away if I leave the Excel object declared with events but do not code any handlers.
In my Dispose I had to swallow the error for my Workbooks.Close and Quit as they were the statements that caused the error.
不,您不应该在析构函数中访问任何托管对象:这包括 COM RCW。
相反,实现标准 IDisposable 模式,并在 Dispose(bool) 方法中释放 COM 对象,就像释放一次性托管对象一样。
No, you should not be accessing any managed objects in a destructor: this includes COM RCWs.
Instead, implement the standard IDisposable pattern, and release your COM objects in the Dispose(bool) method like you would a disposable managed object.