我正在使用Microsoft.interropt.Excel DLL编写Excel类。我完成了所有函数,但在析构函数中出现错误。我想保存对文件的所有更改,并释放所有源代码。我想在析构函数中释放所有源代码。但在析构函数Excel.ApplicationClass中,Workbook和Worksheet对象由一个异常填充,该异常包含消息“无法使用已与其底层RCW分离的COM对象”。因此,我可以't保存什么,关闭什么,因为无法访问工作簿或工作表对象.我不能在析构函数中访问类的私有成员吗?
ddhy6vgd1#
NET与析构函数最接近的是.NET称为终结器的东西。主要区别在于析构函数通常具有确定性终结(例如,当对象上的引用计数变为零时),而.NET终结器在对象不再被引用之后的不确定时间被调用。这由.NET垃圾收集器使用根跟踪过程来处理,而不是使用简单的引用计数。Garbage Collection: Automatic Memory Management in the Microsoft .NET Framework是这方面最好的文章之一,有关终结器的详细信息,请参阅MSDN中的文章Finalize Methods and Destructors。我不能在析构函数中访问类的私有成员吗?不,你这样做不安全。在您的示例中,当对象不再直接或间接地被根引用时,对象引用的COM对象(即私有字段引用的对象)也不会被根引用。(被对象的字段引用并不能使这些COM对象保持活动状态,因为对象不再被引用或跟踪,根,因此COM对象也不会从根开始跟踪。)因此,您的对象及其引用的所有COM对象都已准备好 * 同时 * 进行垃圾收集。一段时间后,垃圾收集器将清理您的对象并调用其终结器,就像它对COM对象所做的那样,每个对象实际上都是一个Runtime Callable Wrapper (RCW)。问题在于,不仅这些对象被垃圾收集的时间不确定,而且调用终结器的 * 顺序 * 也不确定。在这种情况下,运行时可调用 Package 器也有一个终结器,它对自身调用Marshal.ReleaseComObject。其结果是递减围栏COM侧的引用计数,从而可以释放该COM对象。但是由于终结器被调用的顺序是不确定的,所以很有可能你的对象引用的COM对象的终结器会在你的对象的终结器之前触发。所以你的终结器中的代码有时候可以工作,但是,大多数时候,对象引用的一个或多个运行时可调用 Package 将已经调用了它们的终结器,并且在终结器开始执行其代码之前,基础COM对象将已经被释放。简而言之,一般情况下应避免使用终结器,并且永远不应从终结器中访问引用类型,因为这些引用类型可能已经被终结。为了补救你的情况,我会考虑两种不同的可能性:1.在创建COM对象的同一个方法中处理它们。我在这里和here有一些讨论。1.通过使用IDisposable interface而不是依赖非确定性终结器,启用对象的确定性处置。有关如何实现IDisposable模式的文章,请参见:
--迈克
ia2d9nvy2#
我不确定我是否编写错了什么--试着按照这里的例子来做。我发现当我利用IDisposable模式时,一切都能正常工作,除非我需要处理工作簿事件。在我的场景中,用户可以在关闭应用程序之前关闭工作簿。我声明了Excel对象WithEvents,并编写了WorkbookBeforeClose处理程序以满足要求。在这种情况下,当我关闭应用程序(并且我已经关闭Excel)时,我收到“无法使用已与其底层RCW分离的COM对象”错误。当Finalize调用Dispose(False)时,错误发生在Finalize中。如果保留用事件声明的Excel对象,但不编写任何处理程序,则问题就会消失。在我的处置中,我不得不吞下我的工作簿的错误。关闭和退出,因为它们是导致错误的语句。
wbrvyc0a3#
不,您不应该在析构函数中访问任何托管对象:这包括COM RCW。相反,实现标准的IDisposable模式,并像释放可释放托管对象一样在Dispose(bool)方法中释放COM对象。
3条答案
按热度按时间ddhy6vgd1#
NET与析构函数最接近的是.NET称为终结器的东西。主要区别在于析构函数通常具有确定性终结(例如,当对象上的引用计数变为零时),而.NET终结器在对象不再被引用之后的不确定时间被调用。这由.NET垃圾收集器使用根跟踪过程来处理,而不是使用简单的引用计数。
Garbage Collection: Automatic Memory Management in the Microsoft .NET Framework是这方面最好的文章之一,有关终结器的详细信息,请参阅MSDN中的文章Finalize Methods and Destructors。
我不能在析构函数中访问类的私有成员吗?
不,你这样做不安全。
在您的示例中,当对象不再直接或间接地被根引用时,对象引用的COM对象(即私有字段引用的对象)也不会被根引用。(被对象的字段引用并不能使这些COM对象保持活动状态,因为对象不再被引用或跟踪,根,因此COM对象也不会从根开始跟踪。)因此,您的对象及其引用的所有COM对象都已准备好 * 同时 * 进行垃圾收集。一段时间后,垃圾收集器将清理您的对象并调用其终结器,就像它对COM对象所做的那样,每个对象实际上都是一个Runtime Callable Wrapper (RCW)。
问题在于,不仅这些对象被垃圾收集的时间不确定,而且调用终结器的 * 顺序 * 也不确定。在这种情况下,运行时可调用 Package 器也有一个终结器,它对自身调用Marshal.ReleaseComObject。其结果是递减围栏COM侧的引用计数,从而可以释放该COM对象。但是由于终结器被调用的顺序是不确定的,所以很有可能你的对象引用的COM对象的终结器会在你的对象的终结器之前触发。所以你的终结器中的代码有时候可以工作,但是,大多数时候,对象引用的一个或多个运行时可调用 Package 将已经调用了它们的终结器,并且在终结器开始执行其代码之前,基础COM对象将已经被释放。
简而言之,一般情况下应避免使用终结器,并且永远不应从终结器中访问引用类型,因为这些引用类型可能已经被终结。
为了补救你的情况,我会考虑两种不同的可能性:
1.在创建COM对象的同一个方法中处理它们。我在这里和here有一些讨论。
1.通过使用IDisposable interface而不是依赖非确定性终结器,启用对象的确定性处置。
有关如何实现IDisposable模式的文章,请参见:
--迈克
ia2d9nvy2#
我不确定我是否编写错了什么--试着按照这里的例子来做。我发现当我利用IDisposable模式时,一切都能正常工作,除非我需要处理工作簿事件。
在我的场景中,用户可以在关闭应用程序之前关闭工作簿。我声明了Excel对象WithEvents,并编写了WorkbookBeforeClose处理程序以满足要求。
在这种情况下,当我关闭应用程序(并且我已经关闭Excel)时,我收到“无法使用已与其底层RCW分离的COM对象”错误。当Finalize调用Dispose(False)时,错误发生在Finalize中。
如果保留用事件声明的Excel对象,但不编写任何处理程序,则问题就会消失。
在我的处置中,我不得不吞下我的工作簿的错误。关闭和退出,因为它们是导致错误的语句。
wbrvyc0a3#
不,您不应该在析构函数中访问任何托管对象:这包括COM RCW。
相反,实现标准的IDisposable模式,并像释放可释放托管对象一样在Dispose(bool)方法中释放COM对象。