Безопасное удаление объектов взаимодействия Excel в C#?

Я работаю над приложением 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); 

на данный момент я не думаю, что можно закрыть Excel от Visual Studio 2008. Должно быть, это ошибка или что-то в этом роде, но я попробовал 20 лучших сайтов на этом и получил тот же результат: Excel почему-то открывает два экземпляра и когда я делаю сбор мусора и т.д.. (или нет), он закрывает только один экземпляр.

когда я пытаюсь открыть файл, он говорит, что произошла ошибка или он поврежден.

когда я иду в диспетчер задач и убиваю процесс Excel, файл открывается без проблем.]

Есть ли способ закрыть Excel с Visual Studio 2008? Если да, не могли бы вы дать мне руководство или решение этой проблемы?

2 ответа

Сначала я представлю модифицированный 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, чтобы упростить эту задачу.

Убедитесь, что в вашем коде есть две проблемы:

  • Что при закрытии программы Excel остается запущенным процессом
  • Когда вы открываете файл Excel, который создает ваша программа, вы видите ошибку в Excel, говорящую, что файл поврежден или что-то подобное

Я скопировал обработчик щелчка button1 и PST's releaseObject метод в вашем отредактированном вопросе в чистое приложение Winform C2008, C#3.5 и внес несколько небольших изменений, чтобы устранить обе проблемы, перечисленные выше.

Чтобы исправить Excel не выгружается из памяти, позвоните releaseObject на range объект, который вы создали. Сделайте это до вашего звонка releaseObject(xlWorkSheet); Запоминание всех этих ссылок делает программирование COM Interop таким интересным.

Чтобы исправить проблему с поврежденным файлом Excel, обновите WorkBook.SaveAs вызов метода для замены второго параметра (Excel.XlFileFormat.xlXMLSpreadsheet) с Type.Missing, SaveAs Метод будет обрабатывать это правильно по умолчанию.

Я уверен, что код, который вы разместили в своем вопросе, упрощен, чтобы помочь устранить проблемы, с которыми вы столкнулись. Вы должны использовать try..finally блок pst демонстрирует.

Другие вопросы по тегам