При автоматизации бизнес-процессов в среде «1С:Предприятие» разработчики регулярно сталкиваются с необходимостью импорта данных из электронных таблиц Microsoft Excel. Одним из наиболее популярных и простых способов решения этой задачи является использование встроенного объекта ТабличныйДокумент и его метода Прочитать(). Однако на практике программисты нередко обнаруживают неприятный побочный эффект: после выполнения программного кода в Диспетчере задач операционной системы продолжает висеть активный процесс excel.exe. Этот «зависший» процесс не только непроизводительно расходует оперативную память сервера или локальной машины, но и может блокировать файлы для дальнейшего редактирования другими пользователями.
В рамках этой статьи мы подробно разберем, почему возникает подобная проблема, как устроена внутренняя механика платформы «1С:Предприятие» при работе с файлами табличных документов, и рассмотрим практические шаги по предотвращению утечек процессов в операционной системе.
Прежде чем приступать к принудительному завершению процессов, нам необходимо детально разобраться в том, как именно платформа 1С взаимодействует с файлами формата XLS и XLSX. Рассмотрим классический фрагмент кода, который часто вызывает вопросы у начинающих разработчиков:
ТабДок = Новый ТабличныйДокумент;
ТабДок.Прочитать(ЭтотОбъект.ИмяФайла, СпособЧтенияЗначенийТабличногоДокумента.Значение);
Многие полагают, что при вызове метода Прочитать() операционная система Windows в фоновом режиме запускает установленный пакет Microsoft Office (в частности, приложение Excel) для десериализации данных. Однако это заблуждение.
Начиная с версии платформы 8.3, «1С:Предприятие» работает с файлами электронных таблиц нативно. Это означает, что:
.xls и XML-структуру zip-архивов современных файлов .xlsx.ТабДок.Прочитать() физически не способен породить процесс excel.exe в операционной системе.Если в процессе выполнения вашего кода в Диспетчере задач всё же появляется и зависает процесс Excel, причину нужно искать за пределами нативного метода чтения табличного документа.
Проанализируем три ключевых сценария, при которых операционная система запускает excel.exe во время работы вашей обработки.
Очень часто в одной и той же обработке разработчики комбинируют методы чтения. Например, сначала файл быстро считывается нативным «Табличным документом» для получения базовых данных, а затем для сложного форматирования, выравнивания колонок, работы с макросами или конвертации вызывается COM-объект:
ExcelПриложение = Новый COMОбъект("Excel.Application");
Если при завершении работы с этим COM-объектом была допущена ошибка или не все ссылки на него были корректно уничтожены, процесс останется висеть в памяти навсегда.
Если сам файл Excel, который вы пытаетесь прочитать через ТабличныйДокумент.Прочитать(), содержит внедренные OLE-объекты (например, сложные диаграммы сторонних приложений, активные элементы управления, сводные таблицы, связанные с внешними источниками данных), операционная система может автоматически запустить ассоциированное приложение (в данном случае Excel) для корректной отрисовки и инициализации структуры документа. Процесс будет оставаться в памяти до тех пор, пока жив сам объект ТабличныйДокумент в оперативной памяти 1С.
Если импорт выполняется через сторонние внешние компоненты или библиотеки (например, разработанные на C# или C++), они могут неявно обращаться к библиотекам автоматизации Office, что также приводит к неконтролируемому запуску фоновых процессов.
Рассмотрим детально методы устранения этой проблемы в зависимости от того, как именно вы работаете с файлом.
Если вы используете исключительно нативный метод 1С без явного вызова COM, но подозреваете, что процесс блокируется из-за OLE-компонентов внутри открытого файла, необходимо принудительно заставить сборщик мусора 1С очистить занимаемую память.
Для этого сразу после завершения цикла обработки данных листа выполните явное обнуление переменной табличного документа:
// Открываем и обрабатываем файл
ТабДок = Новый ТабличныйДокумент;
ТабДок.Прочитать(ЭтотОбъект.ИмяФайла, СпособЧтенияЗначенийТабличногоДокумента.Значение);
// Код обхода строк и ячеек табличного документа...
// ...
// Принудительно освобождаем память и уничтожаем объект
ТабДок = Неопределено;
Присвоение переменной значения Неопределено мгновенно уничтожает ссылку на объект ТабличныйДокумент. Сборщик мусора платформы 1С сразу высвобождает заблокированные дескрипторы файлов и связанные системные OLE-ресурсы, что приводит к автоматическому закрытию фонового процесса Excel, если он был вызван операционной системой.
Если в вашем коде все-таки присутствует обращение к Excel через COM, процесс зависает из-за так называемой «утечки ссылок» (Reference Counting). Если в коде осталась активной хотя бы одна переменная, ссылающаяся на ячейку, диапазон (Range), лист (Worksheet) или книгу (Workbook), вызов метода Excel.Quit() не приведет к реальному закрытию процесса в Windows.
Разберем эталонный шаблон безопасной работы с COM-объектом Excel, гарантирующий 100% закрытие процесса после выполнения алгоритма:
Попытка
ExcelApp = Новый COMОбъект("Excel.Application");
Исключение
ВызватьИсключение "Не удалось запустить MS Excel. Возможно, он не установлен на компьютере! " + ОписаниеОшибки();
КонецПопытки;
// Отключаем вывод предупреждений и интерактивных окон, чтобы процесс не завис на модальном диалоге
ExcelApp.DisplayAlerts = Ложь;
ExcelApp.Visible = Ложь;
Попытка
Книги = ExcelApp.Workbooks;
РабочаяКнига = Книги.Open(ЭтотОбъект.ИмяФайла);
Лист = РабочаяКнига.Sheets(1);
// Алгоритм чтения данных из ячеек
// Например: Значение = Лист.Cells(1, 1).Value;
// Обязательно закрываем книгу. Параметр "Ложь" указывает закрыть без сохранения изменений
РабочаяКнига.Close(Ложь);
Исключение
// В случае возникновения непредвиденных ошибок логируем их
ЗаписьЖурналаРегистрации("ИмпортДанных", УровеньЖурналаРегистрации.Ошибка, , , ОписаниеОшибки());
КонецПопытки;
// Завершаем работу самого приложения Excel
Если ExcelApp <> Неопределено Тогда
ExcelApp.Quit();
КонецЕсли;
// КРИТИЧЕСКИ ВАЖНО: Уничтожаем ссылки на объекты в обратном порядке их создания!
Лист = Неопределено;
РабочаяКнига = Неопределено;
Книги = Неопределено;
ExcelApp = Неопределено;
// Запускаем принудительный сбор мусора платформы 1С
ВызватьСборМусора();
Важное примечание: Обратите внимание на метод ВызватьСборМусора(). Этот встроенный метод платформы принудительно инициирует очистку неиспользуемых ссылок на COM-объекты во внутреннем пуле памяти 1С. Его вызов в конце тяжелых процедур импорта помогает немедленно выгрузить запущенные экземпляры приложений из памяти.
Иногда возникают ситуации, когда Excel намертво зависает в сессии фонового задания на сервере приложений 1С (например, из-за вывода скрытого окна с предупреждением о нарушении безопасности макросов). В таких критических случаях можно прибегнуть к радикальному средству — принудительному уничтожению процесса через командную строку операционной системы.
Для выполнения этой операции воспользуемся встроенной утилитой Windows taskkill:
КомандаСистемы("taskkill /F /IM excel.exe");
Внимание! Этот метод является крайне агрессивным и имеет ряд серьезных ограничений:
taskkill /F /IM excel.exe завершит абсолютно все процессы Excel на данном компьютере или сервере.Для безопасного удаления конкретного зависшего процесса рекомендуется программно вычислять PID (уникальный идентификатор процесса) именно той копии Excel, которую запустил ваш текущий сеанс 1С, и завершать только этот конкретный процесс по его идентификатору. Однако лучшей практикой все же остается написание чистого и безопасного кода без утечки ссылок, как это было продемонстрировано в Способе 2.
Резюмируя все вышесказанное: если вы работаете через нативный ТабличныйДокумент, не забывайте своевременно обнулять переменную самого документа с помощью ТабДок = Неопределено;, а при использовании COM-соединения — всегда закрывайте книги без сохранения и последовательно очищайте объектные ссылки в блоках Попытка...Исключение.