这种编程模式是 Excel COM 互操作内存泄漏的解决方案吗?

问题描述 投票:0回答:1

我不想对这个主题的神秘主义做出比互联网上看起来更进一步的贡献。我有很多文章讨论这个主题,有些人说永远不要 Marshal.ReleaseComObject 任何东西,有“2 点规则”,yada yada。

事实是,我的 Excel VSTO 插件在长时间使用后确实会变慢并崩溃,而且我不会一直调用 ReleaseComObject。我还(通过计时器)测量了当通过一个属性调用另一个 COM 对象时(例如 hyperlink.Name 立即返回,hyperlink.Range.Address 返回速度慢了约 214 倍),性能受到了巨大的影响,这直观地表明该 COM 对象对象必须/应该调用 Marshal.ReleaseComObject,因为 .NET 的 GC 不会收集它(它只是 RCW,而不是底层 COM 对象)。

完成所有这些后,您认为通过一组具有以下模式的包装类与 Excel 互操作进行交互是否会允许 .NET 的本机 GC 真正再次处理所有内存管理?请不要指出完成这项工作所需的编码工作量(我不在乎,如果它不会让我的程序变慢和崩溃,我会这样做)。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Remoting.Messaging;
using System.Text;
using System.Threading.Tasks;
using Excel = Microsoft.Office.Interop.Excel;
using IRibbon = Microsoft.Office.Core.IRibbonExtensibility;
using App = Microsoft.Office.Interop.Excel.Application;
using Wbs = Microsoft.Office.Interop.Excel.Workbooks;
using Wb = Microsoft.Office.Interop.Excel.Workbook;
using Shs = Microsoft.Office.Interop.Excel.Sheets;
using Ws = Microsoft.Office.Interop.Excel.Worksheet;
using Table = Microsoft.Office.Interop.Excel.ListObject;
using Row = Microsoft.Office.Interop.Excel.ListRow;
using Column = Microsoft.Office.Interop.Excel.ListColumn;
using Cells = Microsoft.Office.Interop.Excel.Range;
using Name = Microsoft.Office.Interop.Excel.Name;
using Chart = Microsoft.Office.Interop.Excel.Chart;
using Shape = Microsoft.Office.Interop.Excel.Shape;
using System.Runtime.InteropServices;

namespace Office.ExcelUtilities
{
    public class ApplicationWrapper
    {
        App _app;
        public ApplicationWrapper(App app)
        {
            _app = app;
        }
        public WorkbookWrapper AddWorkbook()
        {
            Wbs wbs = _app.Workbooks;
            Wb wb = wbs.Add();
            Marshal.ReleaseComObject(wbs);
            return new WorkbookWrapper(wb);
        }
        ~ApplicationWrapper()
        {
            Marshal.ReleaseComObject(_app);
        }
    }
    public class WorkbookWrapper
    {
        Wb _wb = null;
        internal WorkbookWrapper(Wb wb)
        {
            _wb = wb;
        }
        public WorkbookWrapper()
        {
            //just lets client hold this object
            if(_wb == null )
                throw new Exception("You didn't create a workbook right");
        }
        public WorksheetWrapper AddWorksheet(string sheetName)
        {
            Shs wss = _wb.Worksheets;
            Ws ws = wss.Add();
            ws.Name = sheetName;
            Marshal.ReleaseComObject(wss);
            return new WorksheetWrapper(ws);
        }
        ~WorkbookWrapper()
        {
            Marshal.ReleaseComObject(_wb);
        }
    }
    public class WorksheetWrapper
    {
        Ws _ws = null;
        internal WorksheetWrapper(Ws ws)
        {
            _ws = ws;
        }
        public WorksheetWrapper()
        {
            //just lets client hold this object
            if(_ws == null)
                throw new Exception("You didn't create a worksheet right");
        }
        ~WorksheetWrapper()
        {
            Marshal.ReleaseComObject(_ws);
        }
    }
}

这显然不完整,但你明白了。请原谅我对基本互操作类型的重命名。 谢谢!

c# com vsto com-interop excel-interop
1个回答
0
投票

因为 .NET 的 GC 不会收集它(它只是 RCW,而不是 底层 COM 对象)。

不是真的 - GC 释放 COM 对象,你只是无权决定何时发生。当 RCW 对象被释放时,它将

Release()
底层 COM 对象,如果没有其他东西持有对它的引用(引用计数 == 0),COM 对象将销毁自身。

只有在非常特殊的情况下,当您想要完全控制 COM 对象释放的顺序和时间时,才真正需要

Marshal.ReleaseComObject
和多点规则。例如,如果您对 Outlook 文件夹中的所有项目进行循环并访问数百或数千个项目,特别是当 Outlook 处于联机模式时,这会限制与 Exchange Server 的打开 RPC 连接的数量。

就您的具体情况而言,我怀疑这根本不会产生任何影响。

© www.soinside.com 2019 - 2024. All rights reserved.