Фактически вы можете полностью освободить объект приложения 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 для Office (VSTO), в которых используются финализаторы, которые вызывают перемещение всего графа объектов в очередь финализации. Такие объекты не будут освобождены до следующей сборки мусора. Однако, если вы не используете VSTO, вы сможете вызвать GC.Collect()
и GC.WaitForPendingFinalizers()
только один раз.
Я знаю, что явный вызов GC.Collect()
- это запрет (и, конечно, повторение этого дважды звучит очень болезненно), но, честно говоря, нет никакого способа обойти это. Посредством обычных операций вы будете генерировать скрытые объекты, на которые у вас нет ссылок, которые вы, следовательно, не можете освободить никакими другими способами, кроме вызова GC.Collect()
.
Это сложная тема, но на самом деле это все. После того, как вы установите этот шаблон для своей процедуры очистки, вы можете кодировать как обычно, без использования оберток и т. Д. :-)
У меня есть руководство по этому поводу:
Автоматизация программ Office с помощью VB.Net / COM Interop
Он написан для VB.NET, но не отчаивайтесь, принципы точно такие же, как при использовании C #.
person
Community
schedule
01.10.2008