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