При программной обработке табличных документов часто возникает необходимость открыть файл Excel, прочитать из него данные или выполнить модификацию, а затем закрыть документ без сохранения изменений и завершить процесс приложения. На практике разработчики 1С нередко сталкиваются с ошибкой вида «Значение не является значением объектного типа (Quit)» или с ситуацией, когда процесс EXCEL.EXE продолжает «висеть» в диспетчере задач Windows.
Разберем подробно причины возникновения этой ошибки, выясним, как правильно управлять объектной моделью Microsoft Excel через COM, а также рассмотрим надежные способы закрытия файлов и альтернативные варианты чтения данных.
Проанализируем типовую структуру кода, которая приводит к ошибке. В исходном примере функция инициализировала COM-объект приложения, открывала книгу и возвращала только объект книги:
Функция ОткрытиеEXCEL()
Excel = Новый COMОбъект("Excel.Application");
БД = Excel.WorkBooks.Open(ИзначальнаяТаблица);
Возврат БД; // Возвращается объект Workbook, а ссылка на Excel.Application теряется!
КонецФункции
В вызывающей процедуре разработчик пытался закрыть приложение с помощью вызова БДНачальная.Quit(). Поскольку переменная БДНачальная содержит ссылку на объект книги (Workbook), а не на само приложение (Application), платформа 1С выдает ошибку, так как у объекта книги нет метода Quit().
Метод Quit() принадлежит исключительно объекту верхнего уровня — Excel.Application. Для закрытия отдельной книги используется метод Close().
Рассмотрим правильный подход к организации процедур, при котором ссылка на экземпляр приложения сохраняется в контексте вызывающего кода.
Разберем по шагам реализацию:
Excel.Application.Quit().Неопределено, чтобы процесс выгрузился из оперативной памяти.Посмотрим на пример реализации:
&НаКлиенте
Функция ПолучитьОбъектExcel()
Попытка
Excel = Новый COMОбъект("Excel.Application");
// Отключаем видимость окна и системные предупреждения
Excel.Visible = Ложь;
Excel.DisplayAlerts = Ложь;
Возврат Excel;
Исключение
Сообщить("Ошибка при запуске Microsoft Excel: " + ОписаниеОшибки());
Возврат Неопределено;
КонецПопытки;
КонецФункции
&НаКлиенте
Процедура ОбработкаФайлаExcel()
ПриложениеExcel = ПолучитьОбъектExcel();
Если ПриложениеExcel = Неопределено Тогда
Возврат;
КонецЕсли;
// Открываем книгу в режиме "Только для чтения" (3-й параметр ReadOnly = Истина)
Книга = ПриложениеExcel.WorkBooks.Open(ИзначальнаяТаблица, , Истина);
Лист = Книга.WorkSheets(1);
// Выполняем чтение данных...
// Закрываем книгу без сохранения изменений (параметр SaveChanges = Ложь)
Книга.Close(Ложь);
// Завершаем работу приложения Excel
ПриложениеExcel.Quit();
// Очищаем COM-ссылки для освобождения памяти
Лист = Неопределено;
Книга = Неопределено;
ПриложениеExcel = Неопределено;
КонецПроцедуры
Если в коде уже передается только объект книги (Workbook), восстановить доступ к родительскому приложению можно через свойство Application самой книги.
Посмотрим, как выглядит данный способ:
// Получаем доступ к приложению через книгу
ПриложениеExcel = БДНачальная.Application;
ПриложениеExcel.DisplayAlerts = Ложь;
// Закрываем текущую книгу без сохранения
БДНачальная.Close(Ложь);
// Завершаем процесс Excel
ПриложениеExcel.Quit();
// Очищаем переменные
Лист = Неопределено;
БДНачальная = Неопределено;
ПриложениеExcel = Неопределено;
Выясним причину, по которой после вызова Quit() процесс EXCEL.EXE часто продолжает отображаться в диспетчере задач. Технология COM в Windows использует подсчет ссылок. Если в среде 1С хотя бы одна переменная продолжает удерживать ссылку на дочерний объект (например, Лист, Ячейка, UsedRange), процесс не сможет завершиться до тех пор, пока сборщик мусора 1С не уничтожит эту переменную.
Чтобы гарантированно освободить ресурсы, соблюдайте следующие правила:
Истина в качестве третьего параметра метода Open(ПутьКФайлу, , Истина).Close(Ложь).Неопределено сразу после завершения работы с ними.Попытка ... Исключение или гарантируйте его выполнение, чтобы при возникновении ошибки чтения не оставались «зависшие» процессы.Использование COM-объекта Excel.Application имеет существенные ограничения: оно работает только под управлением Windows, требует установленного пакета Microsoft Office, чувствительно к разрядности платформы (32/64 бит) и крайне не рекомендуется к использованию на стороне сервера 1С.
Рассмотрим более производительные и надежные альтернативы:
ТабличныйДокумент: Метод ТабличныйДокумент.Прочитать() позволяет загружать файлы форматов .xls и .xlsx напрямую в память без установленного MS Office, в том числе на сервере и под управлением Linux.
ТабДок = Новый ТабличныйДокумент;
ТабДок.Прочитать(ПутьКФайлу);
// Далее данные считываются через ТабДок.ПолучитьОбласть()
UsedRange (при использовании COM): Если требуется использовать COM, не следует обращаться к каждой ячейке через Cells(Строка, Колонка).Value в цикле, так как это замедляет работу. Чтение всего листа в память выполняется за одно обращение:
МассивДанных = Лист.UsedRange.Value; // Возвращает двумерный SafeArray
// Перебор массива в памяти происходит в десятки раз быстрее межпроцессных вызовов