【问题标题】:Releasing COM Object in Constructor Parameter在构造函数参数中释放 COM 对象
【发布时间】:2015-11-17 14:35:59
【问题描述】:

我正在开发一个 VSTO 插件,现在我希望对其进行优化。在我的代码中,我做了一些事情

public class ExtraOrdinaryClass
{
    public ExtraOrdinaryClass(Excel.Worksheet someGoodSheet)
    {
        tSheetName            = someGoodSheet.Name;
        tDesignSheet          = someGoodSheet;
    }
}

我刚刚知道我应该释放所有 COM 对象,但我正在寻找一种适当的方式来以适当的方式释放 someGoodSheet 对象。我怀疑如果我做下面这样的事情是有效的

public class ExtraOrdinaryClass
{
    public ExtraOrdinaryClass(Excel.Worksheet someGoodSheet)
    {
        tSheetName            = someGoodSheet.Name;
        tDesignSheet          = someGoodSheet;
        Marshal.ReleaseComObject(someGoodSheet);
        someGoodSheet = null;
    }
}

如果我有效地做到这一点,谁能帮助我并告诉我垃圾收集器何时收集参数对象?

【问题讨论】:

    标签: c# .net garbage-collection vsto com-object


    【解决方案1】:

    我刚刚知道我应该释放所有的 COM 对象,但我正在寻找一种适当的方式来以适当的方式释放 someGoodSheet 对象

    不必担心释放 COM 对象,因为您正在编写 VSTO 加载项。 VSTO 加载项是由 COM 应用程序(在本例中为 Excel)加载的in-process COM 库。 Excel 工作表所代表的 COM 对象是由 Excel 创建的,因此它具有所有权。尝试手动释放(通过Marshal.ReleaseComObject)或在您仍然拥有对它的托管引用时过度减少引用计数(如在第二个示例中),可能会使 Excel 和/或您的应用程序崩溃。

    如果您正在编写一个独立的进程,例如通过 COM 启动 Excel 并摆弄一些 COM 对象,那么 是的,您需要确保 COM 对象被适当地释放。

    不要在你的构造函数中使用它:

    Marshal.ReleaseComObject(someGoodSheet);
    

    因为你实际上是在对 COM 说“我已经完成了它”,但你不是因为你仍然有一个通过 tDesignSheet 对它的托管引用。 COM/Excel 可以选择从你下面删除工作表。下次访问tDesignSheet可能会遇到异常。


    在复制到字段的参数上调用Marshal.ReleaseComObject 有点像在调用Dispose() 之后使用Font - 两者都会导致访问已删除对象的不良情况。 (在 COM 的情况下,我假设引用计数达到 0)


    另外,没有必要调用它(因为无论如何你都引用了同一个对象):

    someGoodSheet = null;
    

    您的原始代码(如下所示)很好:

    public class ExtraOrdinaryClass
    {
        public ExtraOrdinaryClass(Excel.Worksheet someGoodSheet)
        {
            tSheetName            = someGoodSheet.Name;  // you arguably don't need this (just read tDesignSheet.Name)          
            tDesignSheet          = someGoodSheet;
        }
    }
    

    并告诉我垃圾收集器何时收集参数对象?

    我想说,在这种情况下,不会收集 .NET 参数,因为现在您在 tDesignSheet 内的 ExtraOrdinaryClass 中有一个额外的引用。您要么需要将 tDesignSheet 设置为 null 并等待 GC 下一次运行,无论何时运行。

    即使收集了 .NET 对象,我怀疑 .NET 也足够聪明,可以知道 COM 对象仍可能被其他本机 COM 客户端(例如 Excel 本身)使用。

    因此,仅仅因为您的 .NET 现在配置的对象不再引用 COM 对象,您可能会发现 COM 对象很可能仍然处于活动状态。例如工作表仍处于打开状态。

    【讨论】:

    • 是的,原来的问题是我释放COM对象的方式好吗?
    • 您是在说“所以,仅仅因为您的 .NET 现在配置的对象不再引用 COM 对象,您可能会发现 COM 对象很可能仍然处于活动状态。例如,工作表仍处于打开状态。 "所以从这个意义上说,我必须释放那个 COM 对象,对吗?而我不应该在构造函数中释放。
    • 查看更新。没有必要。 NET 会自动为您执行此操作,并且考虑到它是在您的构造函数之外的进程内 COM 显式调用 Marshal.ReleaseComObject 是不必要的,因为您不必担心释放内存,特别是当 Excel 仍要保留时工作表打开。
    • @IshamMohamed 如果您愿意,可以将ExtraOrdinaryClass 标记为IDisposable 并在您的处置中调用ReleaseComObject。通常,当他们想要控制 COM 对象何时被删除/关闭(假设它是最后一个引用)和/或出于内存原因时,他们会调用 Marshal.ReleaseComObject()。如果您通过 COM 启动 Word 并希望应用程序完全关闭,那么您会调用 Marshal.ReleaseComObject() 的一个好时机。您可以通过在 app 对象上调用 Marshal.ReleaseComObject() 来实现。
    • ...但在您的情况下,释放工作表不会有太大的作用,因为它最终不会将引用计数减少到零,因为 Excel 仍然引用它。因此,您不妨让 .NET 为您处理 COM。希望这会有所帮助:)
    猜你喜欢
    • 1970-01-01
    • 2012-04-17
    • 1970-01-01
    • 1970-01-01
    • 2018-11-19
    • 2015-09-25
    • 2012-09-17
    • 2012-03-05
    相关资源
    最近更新 更多