Почему не закрывается приложение Excel при работе через COM-объект и как это исправить

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

Проблема зависших процессов Excel.exe в диспетчере задач после завершения работы кода в 1С является классической «болезнью» интеграции через COM-интерфейс. В этой статье мы подробно разберем, почему операционная система продолжает удерживать приложение в памяти, и рассмотрим пошаговые инструкции, которые помогут гарантированно завершать сеанс Excel.

Разбор основной причины: механизм подсчета ссылок

Главная причина, по которой Excel не закрывается, заключается в том, что COM-сервер (которым является Excel) ведет строгий учет всех ссылок на свои объекты. Пока в памяти 1С существует хотя бы одна переменная, ссылающаяся на книгу, лист или даже отдельный диапазон ячеек, основной процесс Excel.exe не завершится, даже если вы вызвали метод Quit(). Проанализируем ситуацию: часто программисты создают неявные ссылки, которые невозможно «обнулить» вручную.

Шаг 1. Соблюдение правила «двух точек»

Рассмотрим распространенную ошибку. Когда мы пишем Лист = Excel.Workbooks.Open(Путь).Sheets(1), мы создаем промежуточный объект Workbooks, на который у нас нет явной переменной. 1С создает его в фоне, и он остается в памяти до полной очистки мусора, удерживая весь процесс Excel. Выясним, как писать код правильно.

Для каждого уровня вложенности необходимо создавать отдельную переменную. Разберем на примере:


// Неправильно:
// Книга = Excel.Workbooks.Open(Файл); 

// Правильно:
ЭксельПриложение = Новый COMОбъект("Excel.Application");
ВсеКниги = ЭксельПриложение.Workbooks; // Создаем отдельную переменную для коллекции
ТекущаяКнига = ВсеКниги.Open(ИмяФайла);
ТекущийЛист = ТекущаяКнига.Sheets(1);

При таком подходе в конце работы мы сможем последовательно уничтожить каждую ссылку в обратном порядке.

Шаг 2. Корректная последовательность закрытия

Просто вызвать Quit() недостаточно. Рассмотрим подробнее правильный алгоритм выхода из приложения. Важно сначала закрыть книгу без сохранения изменений (или с сохранением), а затем завершить работу самого приложения. Посмотрим на код, который минимизирует риск зависания:


// Отключаем интерактивные события перед выходом
ЭксельПриложение.DisplayAlerts = Ложь;
ЭксельПриложение.EnableEvents = Ложь;

// Закрываем книгу. Параметр 0 означает "без сохранения"
Если ТекущаяКнига <> Неопределено Тогда
    ТекущаяКнига.Close(0);
КонецЕсли;

// Выходим из приложения
ЭксельПриложение.Quit();

Шаг 3. Принудительное обнуление переменных

В среде 1С автоматический сборщик мусора не всегда мгновенно реагирует на выход переменной из области видимости. Чтобы ускорить процесс освобождения COM-сервера, проанализируем ситуацию с явным присвоением пустого значения. Программисты часто используют Неопределено или пустую строку.


// Обнуляем переменные в обратном порядке создания
ТекущийЛист = Неопределено;
ТекущаяКнига = Неопределено;
ВсеКниги = Неопределено;
ЭксельПриложение = Неопределено;

Важное примечание: Если ваш код выполняется внутри процедуры, то после завершения работы этой процедуры переменные должны очиститься автоматически. Однако на практике явное обнуление помогает COM-объекту быстрее «понять», что ссылок больше нет.

Шаг 4. Устранение проблем с печатью и буфером обмена

Если в вашем коде используется метод PrintOut(), процесс может зависнуть из-за фоновой печати. Excel пытается передать данные в спулер принтера, а 1С в этот момент уже посылает команду Quit(). В итоге команда игнорируется.

Также проанализируем влияние буфера обмена. Если вы копировали данные внутри Excel, приложение при закрытии может ждать ответа на скрытый вопрос: «Сохранить данные в буфере?». Чтобы избежать этого, используйте команду очистки перед закрытием:


// Очистка режима копирования/вырезания
ЭксельПриложение.CutCopyMode = 0;

Шаг 5. Особенности работы на стороне сервера

Если код выполняется на сервере (в регламентном задании), Excel часто зависает из-за отсутствия папок рабочего стола для системного профиля. Разберем, что нужно сделать системному администратору:

  1. Проверьте наличие папки C:\Windows\System32\config\systemprofile\Desktop.
  2. Для 64-битных систем также проверьте папку C:\Windows\SysWOW64\config\systemprofile\Desktop.
  3. Если этих папок нет, их необходимо создать вручную — это магическим образом решает многие проблемы инициализации COM-объектов под учетной записью System.

Шаг 6. Диагностика через видимость объекта

Если процесс все равно висит, временно измените настройки визуализации. Это позволит увидеть, не зависло ли какое-то диалоговое окно или сообщение об ошибке внутри самого Excel, которое блокирует метод Quit().


XLSОбъект.Visible = Истина;
XLSОбъект.DisplayAlerts = Истина;

Шаг 7. Радикальное решение: завершение процесса по PID

Если никакие программные методы не помогают, можно прибегнуть к принудительному завершению процесса. Однако крайне опасно использовать команду taskkill /f /im excel.exe, так как она закроет файлы у всех пользователей на сервере.

Рассмотрим более безопасный вариант — получение ProcessID конкретного экземпляра Excel через WMI при старте и его точечное завершение в конце. Но прежде чем переходить к таким мерам, попробуйте обернуть блок печати в конструкцию Попытка...Исключение, как советовали участники обсуждения, чтобы выловить скрытые ошибки выполнения методов.

Альтернативный подход

Современным стандартом работы с файлами Excel в 1С является использование объекта ТабличныйДокумент. Он позволяет открывать и сохранять файлы в формате .xlsx напрямую, без использования установленного Microsoft Office и COM-объектов. Рассмотрим этот вариант как наиболее стабильный:


ТабДок = Новый ТабличныйДокумент;
ТабДок.Прочитать(лИмяФайла, СпособЧтенияЗначенийТабличногоДокумента.Значение);
// Выполняем действия с документом
ТабДок.Записать(лИмяФайла, ТипФайлаТабличногоДокумента.XLSX);

Этот метод полностью исключает появление зависших процессов в памяти сервера и работает значительно быстрее.

← На главную