简体   繁体   English

Excel COM对象未释放

[英]Excel COM object not being released

I am trying to have a clean excel break once I load a user's settings file. 加载用户的设置文件后,我试图进行一次干净的Excel中断。 I am running into something I can't figure out other than it has something to do with the dictionaries. 除了遇到与字典有关的问题外,我遇到了其他我无法解决的问题。 If I comment out one (or both) of the Dictionary filling brackets, the settings file loads and then releases, but if both are running, the excel app won't release. 如果我注释掉词典填充括号中的一个(或两个),设置文件将加载然后释放,但是如果两个都在运行,则excel应用程序将不会释放。 Am I also tying excel to the dictionaries by sourcing the data from them? 我是否也通过从字典中获取数据来将excel与字典联系在一起?

I am sure there are other ways to create global dictionaries, but this is the only way I am confidant with at the moment, but I am willing to learn if something better is out there. 我确信还有其他创建全局词典的方法,但这是我目前唯一的知己,但是我愿意学习是否有更好的方法。

The "dictionary filling code" is the for loops with i & j: “字典填充代码”是i&j的for循环:

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);
    }

here is the full code: 这是完整的代码:

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();
    }
  1. You never need to call Marshal.ReleaseComObject in this context. 您无需在这种情况下调用Marshal.ReleaseComObject The runtime is perfectly able to keep track of COM objects and release them when they are no longer referenced. 运行时完全能够跟踪COM对象并在不再引用它们时释放它们。 Calling Marshal.ReleaseComObject is a confusing anti-pattern that sadly even some Microsoft documentation mistakenly suggests. 调用Marshal.ReleaseComObject是一个令人困惑的反模式,可悲的是,甚至某些Microsoft文档也错误地提出了这种反模式。

    In your case, this means those calls in appCleanup should be removed, though you might still have to set application1 = null to clear the reference - your code does not should how this variable gets used. 在您的情况下,这意味着应删除appCleanup那些调用,尽管您可能仍必须设置application1 = null来清除引用-您的代码不应使用此变量。

  2. You should call the garbage collector routine twice - you might have cases where the references form a cycle, and the first GC call will break the cycle, but the COM objects might only get properly released on the second call. 您应该两次调用垃圾收集器例程-您可能遇到引用形成一个循环的情况,并且第一次GC调用将中断该循环,但是COM对象可能仅在第二次调用时才能正确释放。

  3. Finally, you have to be careful with this kind of code in debug builds. 最后,您必须在调试版本中谨慎使用此类代码。 References in a method are artificially kept alive until the end of the method so that they will still be accessible in the debugger. 方法中的引用被人为保留,直到方法结束,以便它们仍可在调试器中访问。 This means your local excelApp variable won't be cleaned up by calling the GC inside that method. 这意味着将无法通过在该方法内调用GC来清除本地excelApp变量。 To avoid this issue, you might follow a pattern like this: 为避免此问题,您可以遵循以下模式:

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

I think maybe you should use string.Copy, because otherwise your dictionaries are holding a reference to string objects linked to the Excel objects. 我认为也许您应该使用string.Copy,因为否则您的字典将保留对链接到Excel对象的字符串对象的引用。 This will create a new instances of the string objects: 这将创建字符串对象的新实例:

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);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM