Как правильно очистить объекты взаимодействия Excel из приложения C#? - PullRequest
1 голос
/ 10 февраля 2020

Этот вопрос задавался и отвечался много раз, например:

Как правильно очистить объекты взаимодействия Excel?

Как утилизировать Правильно взаимодействовать с приложением Excel и рабочей книгой?

Почему Microsoft.Office.Interop.Excel.Application.Quit () оставляет фоновый процесс запущенным?

Как я могу распоряжаться своим приложением Excel

Но ответы Ханса Пассента на следующие вопросы приводят меня к мысли, что они устарели и / или просто неверны:

Очистка объектов взаимодействия Excel с помощью IDisposable

Общие сведения о сборке мусора в. NET

Итак, мой вопрос: как мне очистить Excel взаимодействовать с объектами так, чтобы все управляемые и неуправляемые ресурсы Excel своевременно освобождались (например, когда давление памяти вызывает сборку мусора)?

  • В режиме выпуска?
  • В режиме отладки (если мы заботимся)?

В режиме выпуска:

Достаточно ли просто позволить всем управляемым объектам взаимодействия Excel go из scope?

Нужно ли также вызывать excelApp.Quit ()?

Будет ли давление памяти на неуправляемую кучу вызывать сборку мусора? т.е. мне также нужно позвонить:

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

, чтобы убедиться, что моему управляемому приложению не хватает памяти? Нужно ли мне когда-либо звонить: System.Runtime.InteropServices.Marshal.FinalReleaseComObject (managedExcelObject)?

Пожалуйста, не отвечайте на этот вопрос, если вы не прочитали и не поняли ответы Ханса Пассента.

1 Ответ

0 голосов
/ 01 марта 2020

Поскольку мое взаимодействие с C# в Excel стало более изощренным, у меня начали работать безголовые копии объектов Microsoft Office Excel (32-разрядная версия) в Диспетчере задач после того, как я закрыл свое приложение. Я не нашел никакой комбинации voodoo Marshal.ReleaseComObject () и G C .Collect (), которая бы полностью их устраняла. Я наконец удалил весь код вуду и последовал совету Ханса Пассента. Я смог завершить их в большинстве случаев, когда приложение закрывалось по следующей схеме:

using System;
using System.IO;
using excel = Microsoft.Office.Interop.Excel;

namespace ExcelInterop {
    static class Program {
        // Create only one instance of excel.Application(). More instances create more Excel objects in Task Manager.
        static excel.Application ExcelApp { get; set; } = new excel.Application();

        [STAThread]
        static int Main() {
            try {
                ExcelRunner excelRunner = new ExcelRunner(ExcelApp)
                // do your Excel interop activities in your excelRunner class here
                // excelRunner MUST be out-of-scope when the finally clause executes
                excelRunner = null;  // not really necessary but kills the only reference to excelRunner 
            } catch (Exception e) {
                // A catch block is required to ensure that the finally block excutes after an unhandled exception
                // see: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/try-finally
                Console.WriteLine($"ExcelRunner terminated with unhandled Exception: '{e.Message}'");
                return -1;
            } finally {
                // this must not execute until all objects derived from 'ExcelApp' are out of scope
                if (ExcelApp != null) {
                    ExcelApp.Quit();
                    ExcelApp = null;
                    GC.Collect();
                    GC.WaitForPendingFinalizers();
                }
            }
            Console.WriteLine("ExcelRunner terminated normally");
            return 0;
        }
    }
}

В моем классе ExcelRunner я читаю сотни CSV-файлов в рабочие книги Excel и создаю десятки .xlsx. файлы с таблицами и диаграммами. Я создаю только один экземпляр Microsoft.Office.Interop.Excel.Application () и использую его снова и снова. Чем больше экземпляров, тем больше объектов «Microsoft Office Excel», запущенных в диспетчере задач, и которые необходимо очистить.

Обратите внимание, что пункт finally должен выполняться, чтобы избавиться от безголовых объектов Excel. Приведенный выше шаблон обрабатывает большинство ситуаций закрытия приложений (включая большинство аварий, вызванных необработанными исключениями - но смотрите ВСЕГДА выполняется ли блок C# finally)? ). Одно заметное исключение происходит, когда вы прерываете приложение из отладчика VS (Shift-F5 или красный квадрат «Stop Debugging» на панели инструментов). Если вы прерываете приложение из отладчика, предложение finally не выполняет , а , и объект Excel остается запущенным. Это прискорбно, но я не нашел способа обойти это.

Я проверил это в Visual Studio 2019 и. NET Framework 4.7.2 с использованием взаимодействия Excel 2007 и Excel 2016.

...