Я использую взаимодействие Excel в C # ( ApplicationClass
) и поместил следующий код в мое предложение finally:
while (System.Runtime.InteropServices.Marshal.ReleaseComObject(excelSheet) != 0) { }
excelSheet = null;
GC.Collect();
GC.WaitForPendingFinalizers();
Хотя этот вид работает, Excel.exe
процесс все еще находится в фоновом режиме даже после закрытия Excel. Это только выпущено, как только мое приложение закрыто вручную.
Что я делаю не так, или есть альтернатива для обеспечения правильного удаления объектов взаимодействия?
Ответы:
Excel не завершает работу, потому что ваше приложение все еще содержит ссылки на COM-объекты.
Я предполагаю, что вы вызываете хотя бы один член COM-объекта, не назначая его переменной.
Для меня это был объект excelApp.Worksheets, который я использовал напрямую, не назначая его переменной:
Я не знал, что внутренне C # создал оболочку для COM-объекта Worksheets, который не был освобожден моим кодом (потому что я не знал об этом) и был причиной, по которой Excel не был выгружен.
Я нашел решение моей проблемы на этой странице , где также есть хорошее правило для использования COM-объектов в C #:
Таким образом, с этим знанием правильный способ сделать вышеупомянутое:
ПОСЛЕ MORTEM ОБНОВЛЕНИЕ:
Я хочу, чтобы каждый читатель очень внимательно прочитал этот ответ Ханса Пассанта, так как он объясняет ловушку, в которую я и многие другие разработчики попали. Когда я писал этот ответ несколько лет назад, я не знал о влиянии отладчика на сборщик мусора и сделал неправильные выводы. Я не изменяю свой ответ ради истории, но, пожалуйста, прочитайте эту ссылку и не идите по пути "двух точек": понимание сборки мусора в .NET и очистка объектов взаимодействия Excel с помощью IDisposable
источник
На самом деле вы можете освободить объект приложения Excel аккуратно, но вы должны позаботиться об этом.
Рекомендация сохранять именованную ссылку для абсолютно каждого COM-объекта, к которому вы обращаетесь, и затем явно освобождать его,
Marshal.FinalReleaseComObject()
верна в теории, но, к сожалению, очень сложна в управлении на практике. Если кто-то когда-либо проскальзывает и использует «две точки», или итерирует ячейки с помощьюfor each
цикла или любой другой подобной команды, то у вас будут COM-объекты без ссылок и вы рискуете зависнуть. В этом случае невозможно найти причину в коде; вам придется просмотреть весь ваш код на глаз и, надеюсь, найти причину, задачу, которая может быть почти невозможной для большого проекта.Хорошей новостью является то, что вам не нужно поддерживать именованную переменную для каждого COM-объекта, который вы используете. Вместо этого вызовите
GC.Collect()
и затемGC.WaitForPendingFinalizers()
отпустите все (обычно второстепенные) объекты, на которые у вас нет ссылки, а затем явно освободите объекты, на которые у вас есть ссылка на именованную переменную.Вы также должны выпустить свои именованные ссылки в обратном порядке важности: сначала объекты диапазона, затем рабочие листы, рабочие книги и, наконец, ваш объект приложения Excel.
Например, при условии, что у вас есть именованная переменная объекта Range, именованная переменная
xlRng
Worksheet, именованная переменнаяxlSheet
WorkbookxlBook
и именованная переменная приложения ExcelxlApp
, код очистки может выглядеть примерно так:В большинстве примеров кода, которые вы увидите для очистки COM-объектов из .NET, вызовы
GC.Collect()
andGC.WaitForPendingFinalizers()
делаются ДВАЖДЫ, как в:Это не должно требоваться, однако, если вы не используете Visual Studio Tools for Office (VSTO), который использует финализаторы, которые заставляют весь граф объектов продвигаться в очереди финализации. Такие объекты не будут выпущены до следующей сборки мусора. Однако, если вы не используете VSTO, вы сможете звонить
GC.Collect()
иGC.WaitForPendingFinalizers()
только один раз.Я знаю, что явный вызов
GC.Collect()
- это нет-нет (и, конечно, делать это дважды звучит очень больно), но, честно говоря, пути нет. Посредством обычных операций вы будете создавать скрытые объекты, на которые у вас нет ссылок, которые вы, следовательно, не можете освободить никакими другими способами, кроме вызоваGC.Collect()
.Это сложная тема, но это все, что нужно сделать. После того, как вы установите этот шаблон для своей процедуры очистки, вы можете кодировать как обычно, без использования упаковщиков и т. Д. :-)
У меня есть учебник по этому вопросу здесь:
Автоматизация офисных программ с VB.Net / COM Interop
Он написан для VB.NET, но не стоит откладывать на это, принципы точно такие же, как при использовании C #.
источник
Предисловие: мой ответ содержит два решения, поэтому будьте внимательны при чтении и ничего не пропустите.
Существуют различные способы и рекомендации по загрузке экземпляра Excel, такие как:
Освобождение КАЖДОГО com-объекта явно с помощью Marshal.FinalReleaseComObject () (не забывая о неявно созданных com-объектах). Чтобы освободить каждый созданный com-объект, вы можете использовать правило двух точек, упомянутое здесь:
Как правильно очистить объекты взаимодействия Excel?
Вызов GC.Collect () и GC.WaitForPendingFinalizers (), чтобы заставить CLR освобождать неиспользуемые com-объекты * (На самом деле, это работает, подробности см. В моем втором решении)
Проверка, может ли приложение com-server-application отображать окно сообщения, ожидающее ответа пользователя (хотя я не уверен, что это может помешать закрытию Excel, но я слышал об этом несколько раз)
Отправка сообщения WM_CLOSE в главное окно Excel
Выполнение функции, которая работает с Excel, в отдельном домене приложений. Некоторые люди считают, что экземпляр Excel будет закрыт, когда AppDomain выгружен.
Уничтожение всех экземпляров Excel, которые были созданы после запуска нашего кода взаимодействия с Excel.
НО! Иногда все эти варианты просто не помогают или не могут быть подходящими!
Например, вчера я обнаружил, что в одной из моих функций (которая работает с Excel) Excel продолжает работать после завершения функции. Я перепробовал все! Я тщательно проверил всю функцию 10 раз и добавил Marshal.FinalReleaseComObject () для всего! У меня также были GC.Collect () и GC.WaitForPendingFinalizers (). Я проверил наличие скрытых окон сообщений. Я попытался отправить сообщение WM_CLOSE в главное окно Excel. Я выполнил свою функцию в отдельном домене приложений и выгрузил этот домен. Ничего не помогло! Вариант с закрытием всех экземпляров Excel неуместен, потому что, если пользователь запускает другой экземпляр Excel вручную, во время выполнения моей функции, которая также работает с Excel, этот экземпляр также будет закрыт моей функцией. Бьюсь об заклад, пользователь не будет счастлив! Так что, если честно, это неудачный вариант (без обид, ребята).решение : убить процесс Excel с помощью hWnd его главного окна (это первое решение).
Вот простой код:
Как вы можете видеть, я предоставил два метода в соответствии с шаблоном Try-Parse (я думаю, что здесь уместно): один метод не выдает исключение, если процесс не может быть уничтожен (например, процесс больше не существует) и другой метод генерирует исключение, если процесс не был уничтожен. Единственное слабое место в этом коде - это разрешения безопасности. Теоретически, у пользователя может не быть прав на уничтожение процесса, но в 99,99% случаев у пользователя есть такие права. Я также проверил это с гостевой учетной записью - он отлично работает.
Итак, ваш код, работающий с Excel, может выглядеть так:
Вуаля! Excel прекращен! :)
Хорошо, давайте вернемся ко второму решению, как я и обещал в начале поста. Второе решение - вызвать GC.Collect () и GC.WaitForPendingFinalizers (). Да, они действительно работают, но здесь нужно быть осторожным!
Многие говорят (и я сказал), что вызов GC.Collect () не помогает. Но причина в том, что это не поможет, если есть ссылки на COM-объекты! Одна из самых популярных причин, по которой GC.Collect () не помогает, - это запуск проекта в режиме отладки. В режиме отладки объекты, на которые больше нет ссылок, не будут собирать мусор до конца метода.
Итак, если вы попробовали GC.Collect () и GC.WaitForPendingFinalizers () и это не помогло, попробуйте сделать следующее:
1) Попробуйте запустить свой проект в режиме выпуска и проверьте, правильно ли закрылся Excel
2) Оберните метод работы с Excel в отдельный метод. Итак, вместо чего-то вроде этого:
ты пишешь:
Теперь Excel закроется =)
источник
ОБНОВЛЕНИЕ : добавлен код C # и ссылка на рабочие места Windows
Я потратил некоторое время, пытаясь выяснить эту проблему, и в то время XtremeVBTalk был самым активным и отзывчивым. Вот ссылка на мой оригинальный пост, Чистое завершение процесса Excel Interop, даже если ваше приложение падает . Ниже приведено резюме поста и код, скопированный в этот пост.
Application.Quit()
иProcess.Kill()
работает по большей части, но не удается, если приложения катастрофически сбои. Т.е. в случае сбоя приложения процесс Excel все равно будет запущен.Я обнаружил, что это чистое решение, потому что ОС выполняет реальную работу по очистке. Все, что вам нужно сделать, это зарегистрировать процесс Excel.
Код задания Windows
Оборачивает вызовы Win32 API для регистрации процессов взаимодействия.
Примечание о коде конструктора
info.LimitFlags = 0x2000;
вызывается.0x2000
являетсяJOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
значением перечисления, и это значение определяется MSDN как:Дополнительный Win32 API Вызов для получения идентификатора процесса (PID)
Используя код
источник
Это сработало для проекта, над которым я работал:
Мы узнали, что важно установить для каждой ссылки на COM-объект Excel значение null, когда вы закончили с ним. Это включало Клетки, Листы и все.
источник
Все, что находится в пространстве имен Excel, должно быть освобождено. период
Вы не можете делать:
Вы должны делать
с последующим выпуском объектов.
источник
Во-первых, вам никогда не придется звонить
Marshal.ReleaseComObject(...)
илиMarshal.FinalReleaseComObject(...)
при взаимодействии с Excel. Это противоречивый шаблон, но любая информация об этом, в том числе от Microsoft, которая указывает на то, что вам нужно вручную выпускать ссылки COM из .NET, неверна. Дело в том, что среда выполнения .NET и сборщик мусора правильно отслеживают и очищают ссылки COM. Для вашего кода это означает, что вы можете удалить весь цикл while (...) сверху.Во-вторых, если вы хотите убедиться, что ссылки COM на внепроцессный COM-объект очищаются после завершения процесса (чтобы процесс Excel закрылся), вам необходимо убедиться, что сборщик мусора работает. Вы делаете это правильно с помощью звонков
GC.Collect()
иGC.WaitForPendingFinalizers()
. Вызов этого дважды безопасен и гарантирует, что циклы также будут очищены (хотя я не уверен, что это необходимо, и был бы признателен за пример, который показывает это).В-третьих, при работе под отладчиком локальные ссылки будут искусственно поддерживаться до конца метода (чтобы проверка локальных переменных работала). Поэтому
GC.Collect()
вызовы не эффективны для очистки объекта, какrng.Cells
из того же метода. Вы должны разделить код, выполняющий COM-взаимодействие из очистки GC, на отдельные методы. (Это было ключевым открытием для меня, из одной части ответа, размещенной здесь @nightcoder.)Таким образом, общая схема будет:
Существует много ложной информации и путаницы по этому вопросу, в том числе много сообщений в MSDN и переполнении стека (и особенно этот вопрос!).
В конечном итоге я убедился, что мне стоит присмотреться и найти правильный совет, - пост в блоге Marshal.ReleaseComObject Считается опасным, а также найти проблему со ссылками, оставшимися в живых в отладчике, который запутывал мое предыдущее тестирование.
источник
Я нашел полезный универсальный шаблон, который может помочь реализовать правильный шаблон удаления для COM-объектов, которые должны вызываться Marshal.ReleaseComObject, когда они выходят из области видимости:
Применение:
Шаблон:
Ссылка:
http://www.deez.info/sengelha/2005/02/11/useful-idisposable-class-3-autoreleasecomobject/
источник
Я не могу поверить, что эта проблема преследует мир в течение 5 лет .... Если вы создали приложение, вам необходимо закрыть его, прежде чем удалить ссылку.
при закрытии
Когда вы создаете приложение Excel, оно открывает программу Excel в фоновом режиме. Вы должны дать команду этой программе Excel выйти, прежде чем освободить ссылку, потому что эта программа Excel не является частью вашего прямого контроля. Поэтому он останется открытым, если ссылка будет выпущена!
Хорошее программирование всем ~~
источник
Обычные разработчики, ни одно из ваших решений не помогло мне, поэтому я решил внедрить новый прием .
Сначала позвольте уточнить "Какова наша цель?" => "Не видеть объект Excel после нашей работы в диспетчере задач"
Хорошо. Пусть нет, чтобы бросить вызов и начать уничтожать его, но не пытайтесь уничтожить другой экземпляр Excel, который работает параллельно.
Итак, получите список текущих процессоров и получите PID процессов EXCEL, а затем, как только ваша работа будет выполнена, у нас появится новый гость в списке процессов с уникальным PID, найдите и уничтожьте только этот.
<помните, что любой новый процесс Excel во время вашей работы Excel будет обнаружен как новый и уничтоженный> <Лучшее решение - захватить PID нового созданного объекта Excel и просто уничтожить его>
Это решает мою проблему, надеюсь, ваша тоже.
источник
Похоже, это было слишком сложно. Исходя из моего опыта, для правильного закрытия Excel есть только три ключевых момента:
1: убедитесь, что нет оставшихся ссылок на приложение Excel, которое вы создали (у вас должно быть только одно; установите его на
null
)2: вызов
GC.Collect()
3: Excel должен быть закрыт либо пользователем, вручную закрывающим программу, либо вызовом
Quit
объекта Excel. (Обратите внимание, что онQuit
будет работать так же, как если бы пользователь пытался закрыть программу, и отобразит диалоговое окно подтверждения, если есть несохраненные изменения, даже если Excel не отображается. Пользователь может нажать «Отмена», после чего Excel не будет закрыт. )1 должно произойти раньше, чем 2, но 3 может произойти в любое время.
Один из способов реализовать это состоит в том, чтобы обернуть взаимодействующий объект Excel собственным классом, создать экземпляр взаимодействия в конструкторе и реализовать IDisposable с Dispose, который выглядит примерно так:
Это очистит Excel от вещей вашей программы. После закрытия Excel (вручную пользователем или звонком
Quit
) процесс уйдет. Если программа уже была закрыта, то процесс исчезнет приGC.Collect()
вызове.(Я не уверен, насколько это важно, но вы можете захотеть
GC.WaitForPendingFinalizers()
позвонить послеGC.Collect()
звонка, но избавляться от процесса Excel не обязательно).Это работало для меня без проблем в течение многих лет. Имейте в виду, что хотя это работает, вам на самом деле нужно изящно закрыться, чтобы это работало. Вы по-прежнему будете накапливать процессы Excel.exe, если прервете свою программу до очистки Excel (обычно нажимая «stop» во время отладки вашей программы).
источник
Marshal
.Чтобы добавить причины, по которым Excel не закрывается, даже когда вы создаете прямые ссылки на каждый объект при чтении, создается цикл For.
источник
Я традиционно следовал совету, найденному в ответе ВВС . Тем не менее, пытаясь поддерживать этот ответ в курсе последних опций, я думаю, что все мои будущие проекты будут использовать библиотеку «NetOffice».
NetOffice является полной заменой Office PIA и полностью не зависит от версии. Это коллекция управляемых COM-оболочек, которые могут выполнять очистку, которая часто вызывает такие головные боли при работе с Microsoft Office в .NET.
Некоторые ключевые особенности:
Я никоим образом не связан с проектом; Я просто искренне ценю резкое снижение головных болей.
источник
Принятый ответ здесь является правильным, но также учтите, что следует избегать не только двухточечных ссылок, но и объектов, которые извлекаются через индекс. Вам также не нужно ждать, пока вы закончите с программой для очистки этих объектов, лучше всего создавать функции, которые будут очищать их, как только вы закончите с ними, когда это возможно. Вот функция, которую я создал, которая назначает некоторые свойства объекта Style с именем
xlStyleHeader
:Обратите внимание, что мне пришлось установить
xlBorders[Excel.XlBordersIndex.xlEdgeBottom]
переменную, чтобы очистить это (не из-за двух точек, которые ссылаются на перечисление, которое не нужно освобождать, а потому, что объект, на который я ссылаюсь, на самом деле является объектом Border это должно быть выпущено).Подобные вещи на самом деле не нужны в стандартных приложениях, которые отлично справляются с уборкой, но в приложениях ASP.NET, если вы пропустите хотя бы одно из них, независимо от того, как часто вы вызываете сборщик мусора, Excel будет все еще работает на вашем сервере.
Это требует большого внимания к деталям и выполнения многих тестов при наблюдении за диспетчером задач при написании этого кода, но это избавляет вас от необходимости отчаянного поиска по страницам кода, чтобы найти тот экземпляр, который вы пропустили. Это особенно важно при работе в циклах, где вам нужно освобождать КАЖДУЮ ИНСТАНЦИЮ объекта, даже если он использует одно и то же имя переменной при каждом цикле.
источник
После попытки
GC.Collect()
иGC.WaitForPendingFinalizers()
дважды в концеокончательное решение, которое работает для меня, это переместить один набор
что мы добавили в конец функции в оболочку, как показано ниже:
источник
Я точно следовал этому ... Но я все еще сталкивался с проблемами 1 из 1000 раз. Кто знает почему. Время вытащить молоток ...
Сразу после создания экземпляра класса приложения Excel я получаю только что созданный процесс Excel.
Затем, когда я выполнил все вышеперечисленные действия по очистке COM, я убедился, что процесс не запущен. Если он все еще работает, убейте его!
источник
¸ ° º¤ø „¸ Снимайте Excel proc и жуйте жевательную резинку ¸„ ¤¤º ° ¨
источник
Вы должны знать, что Excel очень чувствителен к культуре, в которой вы работаете.
Вы можете обнаружить, что вам нужно установить язык EN-US перед вызовом функций Excel. Это относится не ко всем функциям - но к некоторым из них.
Это применимо, даже если вы используете VSTO.
Для получения дополнительной информации: http://support.microsoft.com/default.aspx?scid=kb;en-us;Q320369
источник
«Никогда не используйте две точки с COM-объектами» - это хорошее правило, позволяющее избежать утечки COM-ссылок, но Excel PIA может привести к утечке больше, чем кажется на первый взгляд.
Одним из таких способов является подписка на любое событие, представляемое любым из объектов COM модели объекта Excel.
Например, подписка на событие WorkbookOpen класса Application.
Немного теории о событиях COM
Классы COM предоставляют группу событий через интерфейсы обратного вызова. Чтобы подписаться на события, клиентский код может просто зарегистрировать объект, реализующий интерфейс обратного вызова, и класс COM будет вызывать его методы в ответ на определенные события. Поскольку интерфейс обратного вызова является интерфейсом COM, обязанность реализующего объекта - уменьшить счетчик ссылок любого COM-объекта, который он получает (в качестве параметра) для любого из обработчиков событий.
Как Excel PIA представляет события COM
Excel PIA представляет события COM класса приложения Excel как обычные события .NET. Всякий раз , когда код клиента выписывает событие .NET (акцент на «а»), PIA создает в экземпляр класса , реализующего интерфейс обратного вызова и регистрирует его с Excel.
Следовательно, ряд объектов обратного вызова регистрируется в Excel в ответ на различные запросы на подписку из кода .NET. Один объект обратного вызова для каждой подписки на событие.
Интерфейс обратного вызова для обработки событий означает, что PIA должна подписываться на все события интерфейса для каждого запроса подписки на событие .NET. Он не может выбирать. При получении обратного вызова события объект обратного вызова проверяет, заинтересован ли связанный обработчик события .NET в текущем событии или нет, а затем либо вызывает обработчик, либо молча игнорирует обратный вызов.
Влияние на количество ссылок на экземпляры COM
Все эти объекты обратного вызова не уменьшают счетчик ссылок ни одного из COM-объектов, которые они получают (в качестве параметров) для любого из методов обратного вызова (даже для тех, которые игнорируются без предупреждения). Они полагаются исключительно на сборщик мусора CLR для освобождения объектов COM.
Поскольку запуск GC является недетерминированным, это может привести к задержке процесса Excel на более длительный срок, чем требуется, и создать впечатление «утечки памяти».
Решение
На данный момент единственное решение состоит в том, чтобы избежать поставщика событий PIA для класса COM и написать свой собственный поставщик событий, который детерминистически высвобождает объекты COM.
Для класса Application это можно сделать, реализовав интерфейс AppEvents, а затем зарегистрировав реализацию в Excel, используя интерфейс IConnectionPointContainer . Класс Application (и в этом отношении все COM-объекты, отображающие события с использованием механизма обратного вызова) реализует интерфейс IConnectionPointContainer.
источник
Как уже отмечали другие, вам нужно создать явную ссылку для каждого объекта Excel, который вы используете, и вызвать Marshal.ReleaseComObject для этой ссылки, как описано в этой статье базы знаний . Вам также нужно использовать try / finally, чтобы гарантировать, что ReleaseComObject всегда вызывается, даже когда выдается исключение. Т.е. вместо:
вам нужно сделать что-то вроде:
Вам также необходимо вызвать Application.Quit перед освобождением объекта Application, если вы хотите закрыть Excel.
Как вы можете видеть, это быстро становится чрезвычайно громоздким, как только вы пытаетесь сделать что-то даже умеренно сложное. Я успешно разработал .NET-приложения с помощью простого класса-оболочки, который включает несколько простых манипуляций с объектной моделью Excel (открытие рабочей книги, запись в Range, сохранение / закрытие рабочей книги и т. Д.). Класс-оболочка реализует IDisposable, тщательно реализует Marshal.ReleaseComObject для каждого объекта, который он использует, и не публикует какие-либо объекты Excel для остальной части приложения.
Но этот подход не подходит для более сложных требований.
Это большой недостаток .NET COM Interop. Для более сложных сценариев я бы серьезно подумал о написании ActiveX DLL на VB6 или другом неуправляемом языке, которому вы можете делегировать все взаимодействия с внешними COM-объектами, такими как Office. Затем вы можете ссылаться на эту ActiveX DLL из вашего приложения .NET, и все будет намного проще, поскольку вам нужно будет только выпустить эту единственную ссылку.
источник
Когда все вышеперечисленное не сработало, попробуйте дать Excel немного времени, чтобы закрыть его листы:
источник
Убедитесь, что вы освободили все объекты, связанные с Excel!
Я провел несколько часов, пытаясь несколькими способами. Все это отличные идеи, но я наконец-то нашел свою ошибку: если вы не отпустите все объекты, ни один из вышеперечисленных способов не поможет вам в моем случае. Убедитесь, что вы отпустите все объекты, включая дальний!
Варианты вместе здесь .
источник
Отличная статья о выпуске COM-объектов - 2.5. Выпуск COM-объектов. (MSDN).
Метод, который я бы рекомендовал, состоит в том, чтобы обнулить ссылки на Excel.Interop, если они не являются локальными переменными, а затем вызвать
GC.Collect()
иGC.WaitForPendingFinalizers()
дважды. Переменные взаимодействия с локальной областью видимости будут обрабатываться автоматически.Это устраняет необходимость сохранять именованную ссылку для каждого COM-объекта.
Вот пример, взятый из статьи:
Эти слова прямо из статьи:
источник
Правило двух точек не сработало для меня. В моем случае я создал метод для очистки моих ресурсов следующим образом:
источник
Мое решение
источник
Вы должны быть очень осторожны, используя приложения взаимодействия Word / Excel. После того, как мы опробовали все решения, у нас все еще оставалось много процессов «WinWord», которые оставались открытыми на сервере (более 2000 пользователей).
Проработав несколько часов над этой проблемой, я понял, что, если я открою более двух документов
Word.ApplicationClass.Document.Open()
одновременно, используя разные потоки, рабочий процесс IIS (w3wp.exe) завершится с ошибкой, и все процессы WinWord будут открыты!Поэтому я думаю, что нет абсолютного решения этой проблемы, кроме перехода на другие методы, такие как разработка Office Open XML .
источник
Принятый ответ не работал для меня. Следующий код в деструкторе сделал свою работу.
источник
В настоящее время я работаю над автоматизацией Office и наткнулся на решение, которое каждый раз работает для меня. Это просто и не предполагает уничтожения каких-либо процессов.
Кажется, что простым циклом текущих активных процессов и любым способом «доступа» к открытому процессу Excel любой случайный зависший экземпляр Excel будет удален. Приведенный ниже код просто проверяет наличие процессов с именем «Excel», а затем записывает свойство MainWindowTitle процесса в строку. Такое «взаимодействие» с процессом, кажется, заставляет Windows догнать и прервать замороженный экземпляр Excel.
Я запускаю описанный ниже метод непосредственно перед тем, как надстройка, которую я разрабатываю, завершает работу, поскольку она запускает событие выгрузки. Он удаляет все зависшие экземпляры Excel каждый раз. Честно говоря, я не совсем уверен, почему это работает, но он хорошо работает для меня и может быть помещен в конец любого приложения Excel, не беспокоясь ни о двойных точках, Marshal.ReleaseComObject, ни о процессах уничтожения. Я был бы очень заинтересован в любых предложениях относительно того, почему это эффективно.
источник
Я думаю, что отчасти это то, как фреймворк обрабатывает приложения Office, но я могу ошибаться. В некоторые дни некоторые приложения очищают процессы сразу, а в другие дни кажется, что они ждут закрытия приложения. В общем, я перестал обращать внимание на детали и просто убедиться, что в конце дня не будет никаких дополнительных процессов.
Кроме того, и, возможно, я слишком упрощаю вещи, но я думаю, что вы можете просто ...
Как я уже говорил ранее, я не склонен обращать внимание на детали того, когда процесс Excel появляется или исчезает, но это обычно работает для меня. Мне также не нравится держать процессы Excel в течение какого-то другого времени, кроме минимального количества времени, но я, вероятно, просто параноик по этому поводу.
источник
Как, возможно, уже написали некоторые, важно не только, как закрыть Excel (объект); также важно, как вы открываете его, а также по типу проекта.
В приложении WPF в основном один и тот же код работает без или с очень небольшим количеством проблем.
У меня есть проект, в котором один и тот же файл Excel обрабатывается несколько раз для другого значения параметра - например, анализ его на основе значений в общем списке.
Я поместил все связанные с Excel функции в базовый класс, а парсер - в подкласс (разные парсеры используют общие функции Excel). Я не хотел, чтобы Excel открывался и снова закрывался для каждого элемента в общем списке, поэтому я открыл его только один раз в базовом классе и закрыл в подклассе. У меня были проблемы при переносе кода в настольное приложение. Я пробовал многие из вышеупомянутых решений.
GC.Collect()
уже был реализован ранее, дважды, как предложено.Тогда я решил, что переместу код для открытия Excel в подкласс. Вместо того, чтобы открывать только один раз, теперь я создаю новый объект (базовый класс), открываю Excel для каждого элемента и закрываю его в конце. Существует некоторое снижение производительности, но на основании нескольких тестов процессы Excel закрываются без проблем (в режиме отладки), поэтому также удаляются временные файлы. Я продолжу тестирование и напишу еще, если получу обновления.
Суть в следующем: вы также должны проверить код инициализации, особенно если у вас много классов и т. Д.
источник