【问题标题】:Safely disposing Excel interop objects in C#?在 C# 中安全地处理 Excel 互操作对象?
【发布时间】:2012-06-20 04:17:38
【问题描述】:

我正在开发一个 winforms c# visual studio 2008 应用程序。该应用程序与 excel 文件对话,我正在使用 Microsoft.Office.Interop.Excel; 来执行此操作。

我想知道如何确保即使出现错误也释放对象?

这是我的代码:

private void button1_Click(object sender, EventArgs e)
{
    string myBigFile="";
    OpenFileDialog openFileDialog1 = new OpenFileDialog();
    DialogResult result = openFileDialog1.ShowDialog(); // Show the dialog.
    if (result == DialogResult.OK) // Test result.
        myBigFile=openFileDialog1.FileName;

    Excel.Application xlApp;
    Excel.Workbook xlWorkBook;
    Excel.Worksheet xlWorkSheet;
    Excel.Range range;

    string str;
    int rCnt = 0;
    int cCnt = 0;

    xlApp = new Excel.ApplicationClass();
    xlWorkBook = xlApp.Workbooks.Open(myBigFile, 0, true, 5, "", "", true, Microsoft.Office.Interop.Excel.XlPlatform.xlWindows, "\t", true, false, 0, true, 1, 0);
    xlWorkSheet = (Excel.Worksheet)xlWorkBook.Worksheets.get_Item(1);

    range = xlWorkSheet.UsedRange;

    /*
    for (rCnt = 1; rCnt <= range.Rows.Count; rCnt++)
    {
        for (cCnt = 1; cCnt <= range.Columns.Count; cCnt++)
        {
            str = (string)(range.Cells[rCnt, cCnt] as Excel.Range).Value2;
            MessageBox.Show(str);
        }
    }
     */
    xlWorkSheet..EntireRow.Delete(Excel.XLDirection.xlUp)

    xlWorkBook.SaveAs(xlWorkBook.Path + @"\XMLCopy.xls",         Excel.XlFileFormat.xlXMLSpreadsheet, Type.Missing, Type.Missing,
   false, false, Excel.XlSaveAsAccessMode.xlNoChange,
   Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);

    xlWorkBook.Close(true, null, null);
    xlApp.Quit();

    releaseObject(xlWorkSheet);
    releaseObject(xlWorkBook);
    releaseObject(xlApp);
}

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

我如何确保即使在打开工作簿后出现错误,我也要确保处置对象:

Excel.Application xlApp;
Excel.Workbook xlWorkBook;
Excel.Worksheet xlWorkSheet;
Excel.Range range;

换句话说,无论我需要以下几行来运行

xlWorkBook.Close(true, null, null);
xlApp.Quit();

releaseObject(xlWorkSheet);
releaseObject(xlWorkBook);
releaseObject(xlApp);

请注意,我也试过这个,导致同样的问题

xlWorkBook.Close(false, System.Reflection.Missing.Value, System.Reflection.Missing.Value);


                xlApp.Quit();

                Marshal.ReleaseComObject(xlWorkSheet);
                Marshal.ReleaseComObject(xlWorkBook);
                Marshal.ReleaseComObject(xlApp);

                xlWorkSheet = null;
                xlWorkBook = null;
                xlApp = null;

                GC.GetTotalMemory(false);
                GC.Collect();
                GC.WaitForPendingFinalizers();
                GC.Collect();
                GC.GetTotalMemory(true);  

我也这样做了:

GC.Collect()                   ;
                GC.WaitForPendingFinalizers();
                GC.Collect()                  ; 
                GC.WaitForPendingFinalizers();

                Marshal.FinalReleaseComObject(xlWorkSheet);

                xlWorkBook.Close(Type.Missing, Type.Missing, Type.Missing);
                Marshal.FinalReleaseComObject(xlWorkBook); 

                xlApp.Quit();
                Marshal.FinalReleaseComObject(xlApp); 

在这一点上,我认为不可能从 Visual Studio 2008 中关闭 excel。它一定是一个错误或什么的,但我已经尝试了前 20 个网站并得到了相同的结果:excel 正在打开两个实例出于某种原因,当我进行垃圾收集等时。(或不)它只关闭一个实例。

当我尝试打开文件时,它说有错误或已损坏。

当我去任务管理器并杀死excel进程时,文件将毫无问题地打开。]

有没有办法用 Visual Studio 2008 关闭 excel?如果是这样,请您为我提供指导或解决方案

【问题讨论】:

  • 您能告诉我 xlworkbook.close 和 xlapp.quit 与 releaseObject 之间的区别吗?为什么我需要这两个?
  • This 很好地回答了你的问题。
  • @JayRiggs 但 erik 明确表示不要使用 gc.collect
  • @ErikPhilips jay 引用了有人说要使用 gc.collection 两次!

标签: c# excel interop dispose excel-interop


【解决方案1】:

首先我将展示一个修改后的releaseObject,然后我将提供一个使用它的模式。

using Marshal = System.Runtime.InteropServices.Marshal;
private void releaseObject(ref object obj) // note ref!
{
    // Do not catch an exception from this.
    // You may want to remove these guards depending on
    // what you think the semantics should be.
    if (obj != null && Marshal.IsComObject(obj)) {
        Marshal.ReleaseComObject(obj);
    }
    // Since passed "by ref" this assingment will be useful
    // (It was not useful in the original, and neither was the
    //  GC.Collect.)
    obj = null;
}

现在,要使用的模式:

private void button1_Click(object sender, EventArgs e)
{
    // Declare. Assign a value to avoid a compiler error.
    Excel.Application xlApp = null;
    Excel.Workbook xlWorkBook = null;
    Excel.Worksheet xlWorkSheet = null;

    try {
        // Initialize
        xlApp = new Excel.ApplicationClass();
        xlWorkBook = xlApp.Workbooks.Open(myBigFile, 0, true, 5, "", "", true, Microsoft.Office.Interop.Excel.XlPlatform.xlWindows, "\t", true, false, 0, true, 1, 0);
        // If the cast fails this like could "leak" a COM RCW
        // Since this "should never happen" I wouldn't worry about it.
        xlWorkSheet = (Excel.Worksheet)xlWorkBook.Worksheets.get_Item(1);
        ...
    } finally {
        // Release all COM RCWs.
        // The "releaseObject" will just "do nothing" if null is passed,
        // so no need to check to find out which need to be released.
        // The "finally" is run in all cases, even if there was an exception
        // in the "try". 
        // Note: passing "by ref" so afterwords "xlWorkSheet" will
        // evaluate to null. See "releaseObject".
        releaseObject(ref xlWorkSheet);
        releaseObject(ref xlWorkBook);
        // The Quit is done in the finally because we always
        // want to quit. It is no different than releasing RCWs.
        if (xlApp != null) {
            xlApp.Quit();
        }
        releaseObject(ref xlApp);    
    }
}

这种简单的方法可以在大多数情况下扩展/嵌套。我使用实现 IDisposable 的自定义包装类来简化此任务。

【讨论】:

  • 非常感谢您的帮助!我在所有 releaseObject 调用上都收到此错误错误 1 ​​属性或索引器可能不会作为 out 或 ref 参数传递
  • 请注意我使用的是 .net 3.5
  • I__ 是的,在 C# 中,ref 只能与 变量 一起使用。在我的项目中,我有两个“releaseObject”方法。一个使用ref(我将它用于所有变量),另一个不使用“ref”(我很少将它用于某些属性)。如果“ref”未使用,那么只需知道您不能在调用者中更改传入的值。例如。 realeaseObject(ref variable)releaseObjectNonRef(Property); Property = null; 为必填项。
  • 我确实通过删除 REF 来编译它
  • 我的应用打开了两个 EXCEL.exe 实例,我仍然在任务栏中看到这两个实例,您提供的代码没有关闭它们
【解决方案2】:

验证您在代码中看到的两个问题:

  • 当程序关闭时,Excel 仍然是一个正在运行的进程
  • 当您打开程序创建的 Excel 文件时,您会看到一个 Excel 中的错误提示文件已损坏或类似情况

我将您编辑的问题中的 button1 点击处理程序和 pst 的 releaseObject 方法复制到一个干净的 VS2008、C#3.5 Winform 应用程序中,并进行了一些小改动以消除我上面列出的两个问题。

要修复 Excel 无法从内存中卸载的问题,请在您创建的 range 对象上调用 releaseObject。在致电releaseObject(xlWorkSheet); 之前执行此操作记住所有这些引用是 COM 互操作编程如此有趣的原因。

要修复损坏的 Excel 文件问题,请更新您的 WorkBook.SaveAs 方法调用,将第二个参数 (Excel.XlFileFormat.xlXMLSpreadsheet) 替换为 Type.MissingSaveAs 方法默认会正确处理。

我确信您在问题中发布的代码已经过简化,以帮助调试您遇到的问题。您应该使用 try..finally 块 pst 演示。

【讨论】:

  • 非常感谢您!我现在正在尝试。但是请你告诉他们我们使用 xlXML 电子表格的原因是什么?我不希望它是 xml 格式
  • |_ 不确定我是否理解 - 我从您的代码中获得了 xml 格式规范,来自 xlWorkBook.SaveAs 方法调用。我建议不要使用 Type.Missing 代替它。
  • 对不起,我的意思是我想将其保存为与原格式完全相同的格式
  • |_ 我不知道你会怎么做。有一个 Excel Workbook.FileFormat property,您可以在 SaveAs 新文件时检索并可能使用它的值。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多