При программном формировании отчетов, заполнении шаблонов или пакетной выгрузке данных в файлы Microsoft Excel разработчики часто сталкиваются с ошибками совместного доступа. Типичная ситуация: файл сформирован и сохранен, но при попытке тут же прикрепить его к письму для отправки по электронной почте или переместить в другой каталог платформа 1С выдает ошибку блокировки. В диспетчере задач при этом продолжает висеть процесс EXCEL.EXE, удерживающий дескриптор файла.
Проанализируем причины такого поведения COM-сервера, разберем типичные ошибки в коде 1С и рассмотрим по шагам, как гарантированно выгружать Excel из оперативной памяти.
Работа с внешними офисными программами из 1С строится на технологии COM (Component Object Model). COM-сервер отслеживает количество активных ссылок на свои объекты. Если хотя бы одна ссылка не была освобождена, процесс операционной системы не завершится, даже если в коде был вызван метод завершения приложения.
Выделим основные причины зависания процесса:
Worksheets), диапазонам (Range), ячейкам (Cells) или строкам (EntireRow) платформа 1С неявно создает внутренние COM-объекты. Если переменной, содержащей лист или ячейку, не присвоить значение Неопределено, счетчик ссылок COM-сервера остается больше нуля.Close() для каждого открытого экземпляра Workbook до завершения работы самого приложения.Quit() после того, как ссылка на само Приложение уже была затерта значением Неопределено, не сработает. Также метод Quit() не закроет процесс, если перед этим не были закрыты книги и не обнулены промежуточные переменные.Разберем последовательность действий, гарантирующую корректное закрытие файлов и выгрузку процесса из памяти:
Excel.Application обязательно отключаем вывод системных диалогов: Приложение.DisplayAlerts = Ложь;.Попытка ... Исключение, чтобы в случае программного сбоя код закрытия и очистки переменных гарантированно выполнился.Close(Ложь) с параметром Ложь (чтобы отключить запрос на сохранение, если файл уже был записан отдельно).Приложение.Quit() строго до обнуления ссылки на приложение.Неопределено всем переменным, которые ссылались на ячейки, листы, коллекции, книги и само приложение.ОчиститьПамять(), чтобы платформа 1С немедленно освободила память COM-интерфейсов.Посмотрим на пример правильной структуры процедуры:
&НаСервере
Процедура СформироватьИОтправитьФайлExcel()
Приложение = Неопределено;
РабочаяКнига = Неопределено;
Лист = Неопределено;
Ячейка = Неопределено;
Попытка
Приложение = Новый COMОбъект("Excel.Application");
Приложение.DisplayAlerts = Ложь;
Приложение.ScreenUpdating = Ложь;
РабочаяКнига = Приложение.Workbooks.Add();
Лист = РабочаяКнига.Worksheets(1);
Ячейка = Лист.Cells(1, 1);
Ячейка.Value = "Тестовые данные";
ИмяФайла = ПолучитьИмяВременногоФайла("xlsx");
РабочаяКнига.SaveAs(ИмяФайла);
Исключение
Сообщить("Ошибка при формировании Excel: " + ОписаниеОшибки());
КонецПопытки;
// Освобождаем ресурсы в обратном порядке
Ячейка = Неопределено;
Лист = Неопределено;
Если РабочаяКнига <> Неопределено Тогда
РабочаяКнига.Close(Ложь);
РабочаяКнига = Неопределено;
КонецЕсли;
Если Приложение <> Неопределено Тогда
Приложение.Quit();
Приложение = Неопределено;
КонецЕсли;
// Принудительно очищаем COM-ссылки в памяти 1С
ОчиститьПамять();
// Теперь файл гарантированно разблокирован и готов к отправке
ОтправитьФайлПоПочте(ИмяФайла);
КонецПроцедуры
Рассмотрим более надежный и современный вариант выгрузки данных, не требующий установки Microsoft Office и использования технологии COM. В платформе 1С реализован встроенный механизм сохранения табличных документов в формат Excel.
Преимущества данного подхода:
Разберем пример сохранения данных через ТабличныйДокумент:
&НаСервере
Функция СформироватьФайлЧерезТабДок(ВыборкаДанных)
ТабДок = Новый ТабличныйДокумент;
Макет = ПолучитьМакет("МакетВыгрузки");
ОбластьШапка = Макет.ПолучитьОбласть("Шапка");
ТабДок.Вывести(ОбластьШапка);
ОбластьСтрока = Макет.ПолучитьОбласть("Строка");
Пока ВыборкаДанных.Следующий() Цикл
ОбластьСтрока.Параметры.Заполнить(ВыборкаДанных);
ТабДок.Вывести(ОбластьСтрока);
КонецЦикла;
ПутьКФайлу = ПолучитьИмяВременногоФайла("xlsx");
ТабДок.Записать(ПутьКФайлу, ТипФайлаТабличногоДокумента.XLSX);
Возврат ПутьКФайлу;
КонецФункции
Если для бизнес-логики не требуется специфическая функциональность макросов VBA, предпочтительно использовать штатный ТабличныйДокумент вместо создания COM-объектов.