在我的析构函数中释放Excel对象 [英] Release Excel Object In My Destructor

查看:230
本文介绍了在我的析构函数中释放Excel对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Microsoft.Interropt.Excel DLL编写一个Excel类。
我完成所有功能,但我的析构函数中有错误。
我想保存对我的文件的所有更改,我想释放所有的源。我想让所有的人都在我的析构中。
但是在我的析构函数中,Excel.ApplicationClass,Workbook和Worksheet对象被一个异常填充,该异常具有消息COM对象已经与其底层RCW分离不能使用。
所以我无法保存,没有关闭,因为ı无法访问工作簿或工作表对象。



无法访问类私人成员析构函数?

解决方案

.NET对析构函数最接近的是.NET调用终结器。主要区别在于,析构函数通常具有确定性的最终确定(比如,当对象的引用计数变为零时),而.NET终结器在不再被引用对象之后在未确定时被调用。这是由.NET垃圾收集器使用根跟踪过程处理的,而不是使用简单的引用计数。



其中最好的文章之一是垃圾收集:Microsoft .NET Framework中的自动内存管理。有关定稿器的更多信息,请参阅文章 Finalize Methods and Destructors in MSDN。


无法访问析构函数中的private
类成员?


< blockquote>

不,你不能安全地这样做。



你的情况发生了什么,当你的对象不再由根直接或间接引用的对象引用的COM对象 - 也就是您的私有字段引用的对象 - 也不被根引用。 (由对象的字段引用保持这些COM对象存活,因为您的对象不再被根引用,或者从根跟踪,因此COM对象不跟踪根)所以你的对象和它引用的所有COM对象都可以同时被垃圾回收。一段时间后,垃圾收集器将清理您的对象并调用其终结器,因为它也将与COM对象相关联,每个对象真的是一个运行时可调用包装器(RCW)



麻烦的是,不仅仅是时间当这些对象被垃圾收集不确定时,但是调用终结器的顺序也是非确定性的。在这种情况下,运行时可调用包装器也有一个终结器,它调用 Marshal.ReleaseComObject 本身,其结果是减少栅栏COM端的引用计数,从而可以释放此COM对象。但是由于调用finalizer的顺序是不确定的,所以对象引用的COM对象的finalizer很可能会在之前针对对象的finalizer发起。因此,您的终结者中的代码可能有时会工作,但是大多数情况下,您的对象引用的一个或多个运行时可调用包装器将已经调用了其终结器,底层的COM对象将在你的终结者执行代码之前,已经被释放了。



简而言之,你应该避免一般使用finalizer,你不应该从一个finalizer中访问引用类型因为这些参考类型可能已经被定稿。



为了弥补你的情况,我会考虑两种不同的可能性:


  1. 在创建它们的同一方法中处理COM对象。我有几个讨论关于这个这里此处


  2. 通过使用 IDisposable界面,而不是依赖于非确定性的终结者。


关于如何实现IDisposable模式的文章,请参阅:





- Mike


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?

解决方案

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.

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 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:

  1. Dispose of the COM objects within the same method that creates them. I have a couple of discussions on this here and here.

  2. 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:

-- Mike

这篇关于在我的析构函数中释放Excel对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆