При программной работе с таблицами Microsoft Excel через механизм COM-соединения разработчики часто сталкиваются с непредвиденными ошибками в момент завершения сеанса или смены открываемого файла. Типичная ситуация: форма закрывается штатно, но при попытке повторно выбрать файл или закрыть приложение 1С генерирует исключение времени выполнения при обращении к объекту Excel. Разберем по шагам, почему возникает эта проблема и как правильно организовать жизненный цикл COM-объекта в коде.
Проанализируем типовой фрагмент кода, в котором возникает сбой:
Процедура ЗакрытьФайл()
Если Excel <> Неопределено Тогда
Excel.ActiveWorkbook.Close(Ложь);
Excel.Quit();
КонецЕсли;
КонецПроцедуры
На первый взгляд кажется, что проверка Если Excel <> Неопределено полностью защищает процедуру от ошибок. Однако на практике возникает исключение. Выясним причину такого поведения:
Excel.Application был инициализирован, но метод открытия книги не был вызван, завершился с ошибкой или книга не была создана, свойство ActiveWorkbook не содержит COM-объекта книги. Обращение к методу Close() у несуществующего объекта приводит к аварийной остановке выполнения.Excel.Quit() отправляет операционной системе команду закрыть приложение, однако переменная 1С продолжает удерживать ссылку на COM-объект. Если переменной явно не присвоено значение Неопределено, условие Excel <> Неопределено при повторном обращении вернет значение Истина, но сам процесс Excel уже завершен или находится в некорректном состоянии.Рассмотрим простейший способ устранения ошибки непосредственно в существующей процедуре закрытия. Добавим дополнительную проверку свойства ActiveWorkbook и выполним обязательную очистку переменной COM-объекта:
Процедура ЗакрытьФайл()
Если Excel <> Неопределено Тогда
// Проверяем, есть ли действительно открытая книга в приложении
Если Excel.ActiveWorkbook <> Неопределено Тогда
Excel.ActiveWorkbook.Close(Ложь);
КонецЕсли;
Excel.Quit();
// Обязательно освобождаем ссылку в 1С
Excel = Неопределено;
КонецЕсли;
КонецПроцедуры
Такой подход защитит код от сбоев при повторном открытии файла пользователем, а также гарантирует корректное завершение процесса в диспетчере задач операционной системы.
Обращение через свойство ActiveWorkbook считается небезопасной практикой при автоматизации. Если во время работы обработки пользователь переключится в другое окно Microsoft Excel, активной книгой станет совсем другой документ. Разберем более надежную архитектуру работы с сохранением ссылки на открытый экземпляр книги:
Перем ЭкземплярExcel;
Перем ТекущаяКнига;
Процедура ОткрытьФайл(ПутьКФайлу)
// Закрываем предыдущие сеансы перед открытием нового документа
ЗакрытьФайл();
Попытка
ЭкземплярExcel = Новый COMОбъект("Excel.Application");
ЭкземплярExcel.DisplayAlerts = Ложь; // Отключаем предупреждающие сообщения
ТекущаяКнига = ЭкземплярExcel.Workbooks.Open(ПутьКФайлу);
Исключение
ЗакрытьФайл();
ВызватьИсключение "Не удалось открыть файл Excel: " + ОписаниеОшибки();
КонецПопытки;
КонецПроцедуры
Процедура ЗакрытьФайл()
// Последовательно закрываем книгу и приложение
Если ТекущаяКнига <> Неопределено Тогда
Попытка
ТекущаяКнига.Close(Ложь);
Исключение
// Игнорируем ошибку, если файл уже был закрыт
КонецПопытки;
ТекущаяКнига = Неопределено;
КонецЕсли;
Если ЭкземплярExcel <> Неопределено Тогда
Попытка
ЭкземплярExcel.Quit();
Исключение
// Игнорируем ошибку при выходе
КонецПопытки;
ЭкземплярExcel = Неопределено;
КонецЕсли;
КонецПроцедуры
Посмотрим на преимущества данной реализации:
DisplayAlerts = Ложь, предотвращая зависание процесса.Попытка ... Исключение, что гарантирует работу интерфейса без критических падений.При проектировании механизмов загрузки данных важно учитывать технологические ограничения COM-соединения:
Новый COMОбъект работоспособна исключительно в ОС Windows. На сервере или клиенте под управлением Linux данный код вызовет неустранимую ошибку.Если задача заключается исключительно в чтении данных из файла Excel (без сложного редактирования формул или форматирования), рекомендуется использовать встроенный объект 1С ТабличныйДокумент. Разберем пример реализации:
ТабДок = Новый ТабличныйДокумент;
Попытка
ТабДок.Прочитать(ПутьКФайлу);
Исключение
Сообщить("Ошибка чтения файла: " + ОписаниеОшибки());
Возврат;
КонецПопытки;
// Обходим полученные ячейки
Для НомерСтроки = 1 По ТабДок.ВысотаТаблицы Цикл
ЗначениеКолонки = ТабДок.Область(НомерСтроки, 1).Текст;
КонецЦикла;
Метод ТабличныйДокумент.Прочитать() работает как с файлами формата XLS, так и с XLSX. Он полностью кроссплатформенный, выполняется значительно быстрее COM-соединения и не оставляет зависших системных процессов в памяти компьютера.