Excel COM对象未被释放

本文关键字:释放 对象 COM Excel | 更新日期: 2023-09-27 18:12:39

我试图有一个干净的excel打破一旦我加载用户的设置文件。我碰到了一些我不知道的事情,除了和字典有关之外。如果我注释掉一个(或两个)Dictionary填充括号,设置文件加载然后释放,但如果两个都在运行,excel应用程序不会释放。我是否也通过从字典中获取数据来将excel与字典捆绑在一起?

我相信还有其他方法可以创建全局字典,但这是目前我唯一有信心的方法,但如果有更好的方法,我愿意学习。

"字典填充代码"是带有i &j:

for (int i = 0; i < lastRow - 1; i++)
    {
        string key = settingsSheet.Range["B" + (i + 2)].Value; 
        string value = settingsSheet.Range["A" + (i + 2)].Value; 
        DictionaryLoad.DIC.Add(key, value);
    }

完整代码如下:

public Form1()
    {
        InitializeComponent();
        txtFileNamePreface.Enabled = false;
        string fileName = "F:''Shared''Projects''State Assoc Clients''Data Management''Download Site''KeyStats Download Statistics''Naming Conventions.xls";
        LoadProductName(fileName);
    }
    public static class DictionaryLoad
    {
        public static IDictionary<string, string> DIC;
        public static IDictionary<string, string> DIC2;
        static DictionaryLoad()
        {
            DIC = new Dictionary<string, string>();
            DIC2 = new Dictionary<string, string>();
        }
    }
    private void LoadProductName(string fileName)
    {
        //starting up and defining the Excel references
        Excel.Application excelApp = new Excel.Application(); //excel open here
        Excel.Workbook settingsBook = null;
        Excel.Worksheet settingsSheet = null;
        excelApp.Visible = false;
        excelApp.DisplayAlerts = false;
        settingsBook = excelApp.Workbooks.Open(fileName);
        settingsSheet = settingsBook.Sheets["NamingConventions"]; 
        int lastRow = findFirstBlankRow(settingsSheet, "A1") - 1;
        fillComboBox(cbProductType, lastRow, settingsSheet, "A"); 
        fillComboBox(cbYear, lastRow, settingsSheet, "D");
        int lastRow2 = findFirstBlankRow(settingsSheet, "E1");
        fillComboBox(cbRule, lastRow2, settingsSheet, "E");
        for (int i = 0; i < lastRow - 1; i++)
        {
            string key = settingsSheet.Range["B" + (i + 2)].Value; 
            string value = settingsSheet.Range["A" + (i + 2)].Value; 
            DictionaryLoad.DIC.Add(key, value);
        }
        cbProductName.Items.Clear();
        foreach (KeyValuePair<string, string> entry in DictionaryLoad.DIC)
        {
            if (entry.Value == cbProductType.Text)
            { cbProductName.Items.Add(entry.Key); }
        }
        try { cbProductName.SelectedIndex = 0; }
        catch { }
        for (int j = 0; j < lastRow - 1; j++)
        {
            string key = settingsSheet.Range["B" + (j + 2)].Value; 
            string value = settingsSheet.Range["C" + (j + 2)].Value;
            DictionaryLoad.DIC2.Add(key, value);
        }
        cbRule.SelectedIndex = 0;
        cbYear.Text = DateTime.Now.Year.ToString();
        cbQuarter.SelectedIndex = 0;
        cbMonth.Text = DateTime.Now.ToString("MMMM");
        cbProductType.SelectedIndex = 0;
        string workBookName = excelApp.ActiveWorkbook.FullName;
        txtOutputFolder.Text = Path.GetDirectoryName(workBookName);
        settingsBook.Close();
        excelApp.Quit();
        appCleanup(excelApp);
        appCleanup(settingsBook);
        appCleanup(settingsSheet);
        garbageCleanup();
        Application.Exit();
    }
   public void appCleanup(object application1, object application2 = null, object application3 = null)
    {
        Marshal.ReleaseComObject(application1);
        application1 = null;
    }
    public void garbageCleanup()
    {
        GC.Collect();
        GC.WaitForPendingFinalizers();
    }

Excel COM对象未被释放

  1. 在这种情况下不需要调用Marshal.ReleaseComObject。运行时完全能够跟踪COM对象,并在不再引用它们时释放它们。调用Marshal.ReleaseComObject是一个令人困惑的反模式,可悲的是,甚至一些微软文档都错误地建议。

    在您的情况下,这意味着应该删除appCleanup中的那些调用,尽管您可能仍然需要设置application1 = null来清除引用-您的代码不应该如何使用该变量。

  2. 您应该调用垃圾收集器例程两次-您可能会遇到引用形成循环的情况,并且第一次GC调用将打破循环,但是COM对象可能仅在第二次调用时才被正确释放。

  3. 最后,在调试构建中必须小心处理这类代码。方法中的引用被人为地保持活动状态,直到方法结束,以便在调试器中仍然可以访问它们。这意味着您的本地excelApp变量不会通过调用该方法中的GC来清理。为了避免这个问题,您可以遵循这样的模式:

    public void LoadProductNameAndCleanup(string fileName)
    {
        LoadProductName(fileName);
        garbageCleanup();
        garbageCleanup();
    }
    

我想也许你应该使用字符串。复制,因为否则你的字典将保存对链接到Excel对象的字符串对象的引用。这将创建一个字符串对象的新实例:

for (int i = 0; i < lastRow - 1; i++)
{
    string key = string.Copy(settingsSheet.Range["B" + (i + 2)].Value); 
    string value = string.Copy(settingsSheet.Range["A" + (i + 2)].Value); 
    DictionaryLoad.DIC.Add(key, value);
}