Как правильно закрыть Excel и завершить процесс без сохранения изменений в 1С?

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

При программной обработке табличных документов часто возникает необходимость открыть файл Excel, прочитать из него данные или выполнить модификацию, а затем закрыть документ без сохранения изменений и завершить процесс приложения. На практике разработчики 1С нередко сталкиваются с ошибкой вида «Значение не является значением объектного типа (Quit)» или с ситуацией, когда процесс EXCEL.EXE продолжает «висеть» в диспетчере задач Windows.

Разберем подробно причины возникновения этой ошибки, выясним, как правильно управлять объектной моделью Microsoft Excel через COM, а также рассмотрим надежные способы закрытия файлов и альтернативные варианты чтения данных.

Причина ошибки: разница между Application и Workbook

Проанализируем типовую структуру кода, которая приводит к ошибке. В исходном примере функция инициализировала COM-объект приложения, открывала книгу и возвращала только объект книги:


Функция ОткрытиеEXCEL()
    Excel = Новый COMОбъект("Excel.Application"); 
    БД = Excel.WorkBooks.Open(ИзначальнаяТаблица); 
    Возврат БД; // Возвращается объект Workbook, а ссылка на Excel.Application теряется!
КонецФункции

В вызывающей процедуре разработчик пытался закрыть приложение с помощью вызова БДНачальная.Quit(). Поскольку переменная БДНачальная содержит ссылку на объект книги (Workbook), а не на само приложение (Application), платформа 1С выдает ошибку, так как у объекта книги нет метода Quit().

Метод Quit() принадлежит исключительно объекту верхнего уровня — Excel.Application. Для закрытия отдельной книги используется метод Close().

Способ 1. Сохранение ссылки на объект приложения Excel

Рассмотрим правильный подход к организации процедур, при котором ссылка на экземпляр приложения сохраняется в контексте вызывающего кода.

Разберем по шагам реализацию:

  1. Функция создания COM-объекта возвращает само приложение Excel.Application.
  2. Основная процедура открывает книгу, выполняет обработку строк.
  3. После завершения цикла книга закрывается без сохранения, а приложение завершает работу через Quit().
  4. Обязательно очищаются ссылки на COM-объекты значением Неопределено, чтобы процесс выгрузился из оперативной памяти.

Посмотрим на пример реализации:


&НаКлиенте
Функция ПолучитьОбъект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 = Неопределено;
КонецПроцедуры

Способ 2. Завершение работы через свойство Application объекта книги

Если в коде уже передается только объект книги (Workbook), восстановить доступ к родительскому приложению можно через свойство Application самой книги.

Посмотрим, как выглядит данный способ:


// Получаем доступ к приложению через книгу
ПриложениеExcel = БДНачальная.Application;
ПриложениеExcel.DisplayAlerts = Ложь;

// Закрываем текущую книгу без сохранения
БДНачальная.Close(Ложь);

// Завершаем процесс Excel
ПриложениеExcel.Quit();

// Очищаем переменные
Лист = Неопределено;
БДНачальная = Неопределено;
ПриложениеExcel = Неопределено;

Почему процесс EXCEL.EXE остается в памяти и как этого избежать?

Выясним причину, по которой после вызова Quit() процесс EXCEL.EXE часто продолжает отображаться в диспетчере задач. Технология COM в Windows использует подсчет ссылок. Если в среде 1С хотя бы одна переменная продолжает удерживать ссылку на дочерний объект (например, Лист, Ячейка, UsedRange), процесс не сможет завершиться до тех пор, пока сборщик мусора 1С не уничтожит эту переменную.

Чтобы гарантированно освободить ресурсы, соблюдайте следующие правила:

  1. Открывайте файлы только для чтения, передавая Истина в качестве третьего параметра метода Open(ПутьКФайлу, , Истина).
  2. Всегда явно закрывайте книгу методом Close(Ложь).
  3. Присваивайте всем переменным, содержащим COM-объекты, значение Неопределено сразу после завершения работы с ними.
  4. Помещайте блок закрытия и очистки в секцию Попытка ... Исключение или гарантируйте его выполнение, чтобы при возникновении ошибки чтения не оставались «зависшие» процессы.

Альтернативные способы чтения Excel без использования COM

Использование COM-объекта Excel.Application имеет существенные ограничения: оно работает только под управлением Windows, требует установленного пакета Microsoft Office, чувствительно к разрядности платформы (32/64 бит) и крайне не рекомендуется к использованию на стороне сервера 1С.

Рассмотрим более производительные и надежные альтернативы:

  1. Штатный объект ТабличныйДокумент: Метод ТабличныйДокумент.Прочитать() позволяет загружать файлы форматов .xls и .xlsx напрямую в память без установленного MS Office, в том числе на сервере и под управлением Linux.
    
    ТабДок = Новый ТабличныйДокумент;
    ТабДок.Прочитать(ПутьКФайлу);
    // Далее данные считываются через ТабДок.ПолучитьОбласть()
    
  2. Пакетное чтение диапазона через UsedRange (при использовании COM): Если требуется использовать COM, не следует обращаться к каждой ячейке через Cells(Строка, Колонка).Value в цикле, так как это замедляет работу. Чтение всего листа в память выполняется за одно обращение:
    
    МассивДанных = Лист.UsedRange.Value; // Возвращает двумерный SafeArray
    // Перебор массива в памяти происходит в десятки раз быстрее межпроцессных вызовов
    
← На главную