*每个*Excel互操作对象是否需要使用Marshal.ReleaseComObject释放?

dev*_*xer 28 .net c# com excel office-interop

编辑

另请参阅如何正确清理Excel互操作对象?.我最近遇到了这个问题,它提供了很多关于如何正确处理COM对象的问题的见解.绝对检查超出第一个(标记的)答案,因为其他答案超出了简单的"不要使用两个点"和" ReleaseComObject用于每个com对象"的建议.

我首先重新审视了这个问题,因为我意识到尽管注册和处理所有COM对象非常彻底,但我的Excel实例仍然没有得到妥善处理.事实证明,有些方法可以创建完全不明显的COM对象(即,即使您从不使用两个点,也可以错过COM对象).此外,即使您是彻底的,如果您的项目增长超过一定的大小,错过COM对象的机会接近100%.当发生这种情况时,很难找到你错过的那个.上面链接的问题的答案提供了一些其他技术,以确保Excel实例肯定关闭.同时,我对我ComObjectManager(下面)做了一个小的(但重要的)更新,以反映我从上面链接的问题中学到的东西.

原始问题

我已经看到了几个Marshal.ReleaseComObject()与Excel Interop对象一起使用的例子(即来自命名空间Microsoft.Office.Interop.Excel的对象),但我已经看到它用于不同程度.

我想知道我是否可以逃避这样的事情:

var application = new ApplicationClass();
try
{
    // do work with application, workbooks, worksheets, cells, etc.
}
finally
{
    Marashal.ReleaseComObject(application)
}
Run Code Online (Sandbox Code Playgroud)

或者,如果我需要释放创建的每个对象,如此方法:

public void CreateExcelWorkbookWithSingleSheet()
{
    var application = new ApplicationClass();
    var workbook = application.Workbooks.Add(_missing);
    var worksheets = workbook.Worksheets;
    for (var worksheetIndex = 1; worksheetIndex < worksheets.Count; worksheetIndex++)
    {
        var worksheet = (WorksheetClass)worksheets[worksheetIndex];
        worksheet.Delete();
        Marshal.ReleaseComObject(worksheet);
    }
    workbook.SaveAs(
        WorkbookPath, _missing, _missing, _missing, _missing, _missing,
        XlSaveAsAccessMode.xlExclusive, _missing, _missing, _missing, _missing, _missing);
    workbook.Close(true, _missing, _missing);
    application.Quit();
    Marshal.ReleaseComObject(worksheets);
    Marshal.ReleaseComObject(workbook);
    Marshal.ReleaseComObject(application);
}
Run Code Online (Sandbox Code Playgroud)

是什么促使我问这个问题的是,作为我的LINQ奉献者,我真的想做这样的事情:

var worksheetNames = worksheets.Cast<Worksheet>().Select(ws => ws.Name);
Run Code Online (Sandbox Code Playgroud)

...但我担心如果我不释放每个工作表(ws)对象,我将最终导致内存泄漏或重影进程.

任何有关这方面的见解将不胜感激.

更新

基于到目前为止的答案,听起来我真的需要释放我创建的每个com对象.我借此机会建立了一个ComObjectManager课程,让​​它更轻松地解决这个问题.Get()每次实例化一个新的com对象时都必须记住使用该方法,但如果这样做,它将为您处理其他所有事情.如果您发现任何问题,请告诉我(如果可以,请编辑并发表评论).这是代码:

public class ComObjectManager : IDisposable
{
    private Stack<object> _comObjects = new Stack<object>();

    public TComObject Get<TComObject>(Func<TComObject> getter)
    {
        var comObject = getter();
        _comObjects.Push(comObject);
        return comObject;
    }

    public void Dispose()
    {
        // these two lines of code will dispose of any unreferenced COM objects
        GC.Collect();
        GC.WaitForPendingFinalizers();

        while (_comObjects.Count > 0)
            Marshal.ReleaseComObject(_comObjects.Pop());
    }
}
Run Code Online (Sandbox Code Playgroud)

这是一个用法示例:

public void CreateExcelWorkbookWithSingleSheet()
{
    using (var com = new ComObjectManager())
    {
        var application = com.Get<ApplicationClass>(() => new ApplicationClass());
        var workbook = com.Get<Workbook>(() => application.Workbooks.Add(_missing));
        var worksheets = com.Get<Sheets>(() => workbook.Worksheets);
        for (var worksheetIndex = 1; worksheetIndex < worksheets.Count; worksheetIndex++)
        {
            var worksheet = com.Get<WorksheetClass>(() => (WorksheetClass)worksheets[worksheetIndex]);
            worksheet.Delete();
        }
        workbook.SaveAs(
            WorkbookPath, _missing, _missing, _missing, _missing, _missing,
            XlSaveAsAccessMode.xlExclusive, _missing, _missing, _missing, _missing, _missing);
        workbook.Close(true, _missing, _missing);
        application.Quit();
    }
}
Run Code Online (Sandbox Code Playgroud)

EMP*_*EMP 13

我相信你必须在每个COM对象上调用ReleaseComObject.由于它们不是垃圾收集的,因此父子层次结构并不真正进入等式:即使释放父对象,它也不会减少任何子对象的引用计数.

  • 是的,您可以认为那些不是问题.您只需要显式释放COM对象; 标准的.NET类型很好.(顺便说一下你的包装顺便说一句,非常优雅的解决方案.) (4认同)

log*_*cnp 6

您应该在代码中使用的每个COM对象上调用Marshal.ReleaseComObject,而不仅仅是主应用程序对象.