На самом деле вы можете освободить объект приложения Excel аккуратно, но вы должны позаботиться об этом.
Рекомендация сохранять именованную ссылку для абсолютно каждого COM-объекта, к которому вы обращаетесь, и затем явно освобождать ее через Marshal.FinalReleaseComObject()
, теоретически верна, но, к сожалению, очень сложна в управлении на практике. Если кто-то когда-нибудь проскальзывает и использует «две точки», или итерирует ячейки с помощью цикла for each
или любой другой подобной команды, то у вас будут COM-объекты без ссылок и вы рискуете зависнуть. В этом случае невозможно найти причину в коде; вам придется просмотреть весь ваш код на глаз и, надеюсь, найти причину, задачу, которая может оказаться практически невозможной для большого проекта.
Хорошая новость заключается в том, что вам не нужно поддерживать ссылку на именованную переменную для каждого используемого вами COM-объекта. Вместо этого вызовите GC.Collect()
, а затем GC.WaitForPendingFinalizers()
, чтобы освободить все (обычно второстепенные) объекты, на которые вы не держите ссылку, а затем явным образом освободите объекты, на которые у вас есть ссылка на именованную переменную.
Вам также следует выпустить именованные ссылки в обратном порядке важности: сначала выберите объекты диапазона, затем рабочие листы, рабочие книги и, наконец, ваш объект приложения Excel.
Например, если у вас есть переменная объекта Range с именем xlRng
, переменная рабочего листа с именем xlSheet
, переменная Workbook с именем xlBook
и переменная приложения Excel с именем xlApp
, тогда ваш код очистки может выглядеть что-то вроде следующего:
// Cleanup
GC.Collect();
GC.WaitForPendingFinalizers();
Marshal.FinalReleaseComObject(xlRng);
Marshal.FinalReleaseComObject(xlSheet);
xlBook.Close(Type.Missing, Type.Missing, Type.Missing);
Marshal.FinalReleaseComObject(xlBook);
xlApp.Quit();
Marshal.FinalReleaseComObject(xlApp);
В большинстве примеров кода, которые вы увидите для очистки COM-объектов из .NET, вызовы GC.Collect()
и GC.WaitForPendingFinalizers()
делаются ДВАЖДЫ, как в:
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
Однако это не требуется, если только вы не используете Visual Studio Tools for Office (VSTO), в котором используются финализаторы, которые приводят к продвижению всего графа объектов в очереди финализации. Такие объекты не будут освобождены до следующей сборки мусора next . Однако, если вы не используете VSTO, вы сможете звонить GC.Collect()
и GC.WaitForPendingFinalizers()
только один раз.
Я знаю, что явный вызов GC.Collect()
- это нет-нет (и, конечно, делать это дважды звучит очень больно), но, честно говоря, пути нет. При обычных операциях вы будете создавать скрытые объекты, на которые у вас нет ссылок, которые вы, следовательно, не можете освободить никакими другими способами, кроме вызова GC.Collect()
.
Это сложная тема, но это действительно все, что нужно. После того, как вы установите этот шаблон для процедуры очистки, вы можете кодировать как обычно, без использования упаковщиков и т. Д.: -)
У меня есть учебник по этому вопросу:
Автоматизация офисных программ с VB.Net / COM Interop
Он написан для VB.NET, но не отчаивайтесь, принципы такие же, как и при использовании C #.