正确关闭 C# excel COM 对象

Posted

技术标签:

【中文标题】正确关闭 C# excel COM 对象【英文标题】:Closing C# excel COM object properly 【发布时间】:2013-09-23 00:27:40 【问题描述】:

我正在尝试正确关闭 excel COM 对象,例如 excel 应用程序工作簿和工作表。我已经浏览了这个站点和其他站点上的所有解决方案。没有任何效果。唯一的方法是运行一个循环来杀死或超越进程。有没有更好的方法或方法可以只杀死单个 excel 文件进程?

        #region VARIABLE

            int nColumn = 0;
            int nIMPORT = 1;
            int endofsheet = 0;

            string IMPORTfilepath = null;
            string nsheet = null;
            string nCell = null;
            string lastfilename = "";            

            double rRFDS = 0;
            double cRFDS = 0;

            List<String> filename = new List<String>();  

            object misValue1 = System.Reflection.Missing.Value;

            //Opening Central DB
            Excel.Application CentralDB;
            Excel.Workbook CentralDBWorkBook;
            Excel.Worksheet CentralDBWorkSheet;
            Excel.Range CentralDBrange;

            //Opening RFDS
            Excel.Application IMPORT;
            Excel.Workbook IMPORTWorkBook;
            Excel.Worksheet IMPORTWorkSheet;

            //Excel.Range RFDSrange;
            Excel.Range CellAddress;

        #endregion

        CentralDB = new Microsoft.Office.Interop.Excel.Application();
        CentralDBWorkBook = CentralDB.Workbooks.Open(Odl1.FileName, 0, false, 5, "", "", true, Microsoft.Office.Interop.Excel.XlPlatform.xlWindows, "\t", false, false, 0, true, 1, 0);
        CentralDBWorkSheet = CentralDB.Worksheets.get_Item(1);

        IMPORT = new Microsoft.Office.Interop.Excel.Application();
        IMPORTfilepath = Odl2.FileNames[nIMPORT];
        IMPORTWorkBook = IMPORT.Workbooks.Open(IMPORTfilepath, 0, true, 5, "", "", true, Microsoft.Office.Interop.Excel.XlPlatform.xlWindows, "\t", false, false, 0, true, 1, 0);
        IMPORTWorkSheet = IMPORT.Worksheets.get_Item(1);

        CentralDBrange = CentralDBWorkSheet.UsedRange;        

        filename = Odl2.FileNames.ToList();

        endofsheet = CentralDBrange.Rows.Count;            

        foreach(string fn in filename)
        
            try
            
                IMPORTWorkBook = IMPORT.Workbooks.Open(fn, 0, true, 5, "", "", true, Microsoft.Office.Interop.Excel.XlPlatform.xlWindows, "\t", false, false, 0, true, 1, 0);
                CentralDBWorkSheet.Cells[nIMPORT + endofsheet, 1] = IMPORTWorkBook.Name;
                for (nColumn = 3; nColumn <= CentralDBrange.Columns.Count; nColumn++)
                
                    nsheet = (string)(CentralDBrange.Cells[2, nColumn].Value2);
                    foreach (Excel.Worksheet ws in IMPORTWorkBook.Sheets)
                    
                        if (ws.Name.Equals(nsheet))
                        
                            IMPORTWorkSheet = IMPORTWorkBook.Worksheets.get_Item(nsheet);

                            nCell = (string)(CentralDBrange.Cells[3, nColumn].Value2);
                            CellAddress = CentralDBWorkSheet.get_Range(nCell, nCell);

                            rRFDS = CellAddress.Row;
                            cRFDS = CellAddress.Column;
                            CentralDBWorkSheet.Cells[nIMPORT + endofsheet, nColumn] = IMPORTWorkSheet.Cells[rRFDS, cRFDS];
                            label4.Text = nIMPORT.ToString() + "/" + Convert.ToString(Odl2.FileNames.Count());
                        
                    
                
                nIMPORT++;
                nColumn = 3;
                CentralDBWorkBook.Save();
                IMPORTWorkBook.Close(false, null, null);
            
            catch (System.Exception ex)
            
                MessageBox.Show("Unable to release the Object " + ex.ToString());                            
            
            lastfilename = fn;
        

        CentralDB.Quit();
        releaseObject(CentralDBWorkSheet);
        releaseObject(CentralDBWorkBook);
        releaseObject(CentralDB);

        IMPORT.Quit();
        releaseObject(IMPORTWorkSheet);
        releaseObject(IMPORTWorkBook);
        releaseObject(IMPORT);
        GC.Collect();  
         

            private void releaseObject(object obj)
            
                try
                
                    System.Runtime.InteropServices.Marshal.ReleaseComObject(obj);
                    obj = null;
                
                catch (System.Exception ex)
                
                    obj = null;
                    MessageBox.Show("Unable to release the Object " + ex.ToString());
                
                finally
                
                    GC.Collect();
                

            

【问题讨论】:

也许之前的question 可以帮到你。 提示:尽可能少地使用对 Excel 的引用,并且尽量不要将其存储在本地/临时变量中。我还会考虑为 Excel 实例制作单例模式。 【参考方案1】:

试试这个顺序(加上.Visible = false):

    releaseObject(CentralDBWorkSheet);
    releaseObject(CentralDBWorkBook);

    releaseObject(IMPORTWorkSheet);
    releaseObject(IMPORTWorkBook);

    CentralDB.Visible = false;
    IMPORT.Visible = false;

    CentralDB.Quit();
    IMPORT.Quit();

    releaseObject(CentralDB);
    releaseObject(IMPORT);

【讨论】:

使用该订单不走运【参考方案2】:

除了我的评论... 我使用它并且工作得很好......但同样,我没有到处都有参考资料。

private void DoReleaseCOMObject(object obj)
        
            int refCounter = Marshal.ReleaseComObject(obj);
            while (refCounter > 0)
            
                refCounter = Marshal.ReleaseComObject(obj);
                System.Diagnostics.Debug.WriteLine("ReleaseCOM ref: " + refCounter);
            

            obj = null;
        

 private void DoFinalReleaseCOMObject(object obj)
        
            int refCounter = Marshal.FinalReleaseComObject(obj);
            System.Diagnostics.Debug.WriteLine("FinalReleaseComObject ref: " + refCounter);

            obj = null;
        

public void Dispose()
        
            System.Diagnostics.Debug.WriteLine("In Dispose");
            this.Dispose(false); // because we are doing unmanaged
            GC.Collect();
            GC.SuppressFinalize(this);
        

        protected virtual void Dispose(bool disposing)
        
            System.Diagnostics.Debug.WriteLine("In Dispose (" + disposing + ")");
            if (!disposing) // unmanaged
            
                #region COM Cleanup

                this.DoFinalReleaseCOMObject(this.headerStartCell);
                this.DoFinalReleaseCOMObject(this.headerEndCell);
                this.DoFinalReleaseCOMObject(this.headerWriteRange);

                this.DoFinalReleaseCOMObject(this.startCell);
                this.DoFinalReleaseCOMObject(this.endCell);
                this.DoFinalReleaseCOMObject(this.writeRange);

                this.DoFinalReleaseCOMObject(this.ws);
                this.DoFinalReleaseCOMObject(this.wb);
                this.DoFinalReleaseCOMObject(this.wbs);

                this.DoFinalReleaseCOMObject(this.excelApp);

                System.Diagnostics.Debug.WriteLine("I've tried to clean");

                #endregion
            

            // no need for else condition if dispose is true as we are not dealing with managed objects
        

是的,我实现了 IDisposable 接口。我还引用了一些全局引用/持有的 Excel 对象(相同的实例),这就是我退出 Excel 后要清理的内容

【讨论】:

以上是关于正确关闭 C# excel COM 对象的主要内容,如果未能解决你的问题,请参考以下文章

需要澄清正确清理 Excel 互操作对象的答案

如何在c#中将对象转换为数组? [关闭]

无法使用 Interopservices 关闭 excel

C#读取Excel中嵌套的Json对象,Json带斜杠的问题(其一)

c# 操作Excel 图表并设置图表的格式?

Excel的Range对象(C#)