【问题标题】:Release Excel Object In My Destructor在我的析构函数中释放 Excel 对象
【发布时间】:2011-01-06 08:34:25
【问题描述】:

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

我不能在 Destructor 中访问类私有成员吗?

【问题讨论】:

  • 如果您能告诉我们更多关于您的课程的信息,可能会有所帮助。何时调用析构函数 - 应用程序关闭时或其他用户启动的时间?

标签: .net excel release destructor finalizer


【解决方案1】:

.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)

问题在于,不仅这些对象何时被垃圾回收的时间不确定,而且调用终结器的顺序也是不确定的。在这种情况下,Runtime Callable Wrapper 也有一个终结器,它在自身上调用Marshal.ReleaseComObject,其结果是减少栅栏的 COM 端的引用计数,以便可以释放此 COM 对象。但是由于调用终结器的顺序是不确定的,因此对象引用的 COM 对象的终结器很可能会在对象的终结器之前触发。因此,您的终结器中的代码可能有时可以工作,但在大多数情况下,您的对象引用的一个或多个运行时可调用包装器已经调用了它们的终结器,并且底层的 COM 对象将在你的终结器开始执行它的代码之前已经发布了。

简而言之,您应该避免使用终结器,并且永远不要从终结器中访问引用类型,因为这些引用类型可能已经被终结。

为了纠正您的情况,我会考虑两种不同的可能性:

  1. 在创建它们的同一方法中处理 COM 对象。我对此herehere 进行了几次讨论。

  2. 通过使用IDisposable interface 启用对象的确定性处置,而不是依赖于非确定性终结器。

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

-- 迈克

【讨论】:

    【解决方案2】:

    我不确定我的编码是否有误——尝试按照此处的示例进行操作。我发现当我利用 IDisposable 模式时,一切正常,除非我需要处理工作簿事件。

    在我的场景中,用户可以在关闭应用程序之前关闭工作簿。我已经声明了 Excel 对象 WithEvents 并编写了 WorkbookBeforeClose 处理程序以满足要求。

    在这种情况下,当我关闭我的应用程序(并且我已经关闭了 Excel)时,我收到“无法使用已与其底层 RCW 分离的 COM 对象”错误。当调用 Dispose(False) 时,Finalize 中发生错误。

    如果我将 Excel 对象声明为事件但不编写任何处理程序,问题就会消失。

    在我的 Dispose 中,我不得不吞下我的 Workbooks.Close 和 Quit 的错误,因为它们是导致错误的语句。

    【讨论】:

      【解决方案3】:

      不,您不应该在析构函数中访问任何托管对象:这包括 COM RCW。

      相反,实现标准 IDisposable 模式,并在 Dispose(bool) 方法中释放 COM 对象,就像释放一次性托管对象一样。

      【讨论】:

        猜你喜欢
        • 2014-06-05
        • 1970-01-01
        • 2010-10-08
        • 2013-01-04
        • 2015-07-09
        • 2014-08-19
        • 2016-10-25
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多