Как удалить временный файл Excel, если он не успевает закрыться и возникает ошибка доступа?

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

В процессе разработки на платформе 1С:Предприятие 8.3 программисты часто сталкиваются с ситуацией, когда после завершения работы с приложением Microsoft Excel через механизмы COM-автоматизации попытка удалить временный файл приводит к ошибке: "Ошибка совместного доступа к файлу". Это происходит потому, что операционная система не успевает мгновенно завершить процесс excel.exe и освободить дескриптор файла, даже если была вызвана команда Quit(). В этой статье мы подробно разберем, почему это происходит и как правильно организовать код, чтобы избежать подобных блокировок.

Анализируем причину возникновения блокировки

Разберем ситуацию: когда мы инициализируем объект COMОбъект("Excel.Application"), операционная система запускает отдельный процесс. Метод ОбъектExcel.Application.Quit() посылает приложению Excel команду на закрытие. Однако выполнение этой команды в самом Excel происходит асинхронно по отношению к коду 1С. Программа 1С продолжает выполнение следующей строки кода (удаление файла) в то время, как Excel еще может записывать данные в профиль пользователя или закрывать внутренние потоки. Рассмотрим, какие стратегии помогут нам решить эту задачу.

Решение 1. Циклический перебор попыток удаления

Это наиболее простой и часто используемый на практике метод. Если файл занят другим процессом, мы не останавливаем выполнение программы критической ошибкой, а пробуем повторить операцию через небольшие промежутки времени. Проанализируем пример кода, который позволяет дождаться освобождения файла:


ФайлНеУдален = Истина;
КоличествоПопыток = 0;
МаксимальноеКоличествоПопыток = 254; // Ограничиваем цикл, чтобы избежать бесконечного зависания

Пока ФайлНеУдален И (КоличествоПопыток < МаксимальноеКоличествоПопыток) Цикл
    Попытка
        УдалитьФайлы(врИмяФайла);
        ФайлНеУдален = Ложь;
    Исключение
        КоличествоПопыток = КоличествоПопыток + 1;
        // Можно добавить небольшую паузу, если это необходимо
    КонецПопытки;
КонецЦикла;

Как показывает практика, файл обычно освобождается уже на 10–20 итерации, что занимает доли секунды. Этот метод хорош своей надежностью, но стоит помнить о замечании из обсуждения: если данный код выполняется внутри транзакции, то возникновение исключения в блоке Попытка...Исключение может пометить транзакцию как "битую", и последующее выполнение ЗафиксироватьТранзакцию() вызовет ошибку.

Решение 2. Правильное освобождение COM-объекта в 1С

Одной из скрытых причин удержания процесса excel.exe в памяти является наличие живой ссылки на объект в самой 1С. Даже если вы вызвали Quit(), переменная ОбъектExcel все еще содержит ссылку на COM-объект. Пока переменная не будет очищена, 1С не "отпустит" Excel окончательно. Рассмотрим правильную последовательность действий:

  1. Выключаем вывод предупреждений Excel (чтобы процесс не завис на вопросе "Сохранить изменения?").
  2. Закрываем рабочую книгу Workbook.Close().
  3. Вызываем Application.Quit().
  4. Обязательно обнуляем переменную в 1С.

Посмотрим на пример правильного завершения работы:


// Отключаем диалоговые окна
ОбъектExcel.DisplayAlerts = Ложь;

// Закрываем приложение
ОбъектExcel.Quit();

// Принудительно освобождаем ссылку в 1С
ОбъектExcel = Неопределено;

// Теперь можно пробовать удалять файл
УдалитьФайлы(врИмяФайла);

Установка значения Неопределено уничтожает COM-объект в памяти 1С, что посылает сигнал операционной системе о том, что клиент завершил работу с сервером автоматизации.

Решение 3. Использование программной паузы (Таймаут)

Если мы работаем на сервере в контексте управляемых форм, использование метода Приостановить() (которого нет в стандартном наборе функций для сервера) недоступно. Однако мы можем реализовать паузу другими способами. Рассмотрим вариант с использованием объекта WScript.Shell, который позволяет сделать паузу в миллисекундах:


Процедура ПаузаНаСервере(Секунд)
    Скрипт = Новый COMОбъект("WScript.Shell");
    Скрипт.Run("ping 127.0.0.1 -n " + Формат(Секунд + 1, "ЧГ=0"), 0, Истина);
КонецПроцедуры

Использование команды ping — это классический трюк для создания задержки в серверном коде. Параметр -n указывает количество запросов, а флаг Истина в методе Run заставляет 1С ждать завершения выполнения команды. Это позволяет дать Excel время на "честное" закрытие всех дескрипторов.

Решение 4. Работа через Табличный документ (Альтернатива COM)

Выясним, всегда ли нам нужен Excel как COM-объект. Если ваша задача — просто сформировать файл и сохранить его на диск, или прочитать данные из файла .xlsx, платформа 1С предоставляет гораздо более эффективные встроенные средства. Использование объекта ТабличныйДокумент позволяет работать с форматами Excel напрямую, без запуска тяжелого процесса excel.exe.

Проанализируем пример сохранения без использования COM:


ТабДок = Новый ТабличныйДокумент;
// ... заполнение документа данными ...
ТабДок.Записать(ПутьКФайлу, ТипФайлаТабличногоДокумента.XLSX);

Этот метод полностью снимает проблему блокировки файлов, так как запись производится средствами самой платформы. Это работает быстрее, стабильнее и не требует установленного Microsoft Office на сервере.

Рекомендации по настройке серверной среды

Иногда проблема кроется не в коде, а в окружении. Проанализируем системные факторы:

Подводя итог, можно сказать, что наиболее надежным способом удаления файла является сочетание правильного освобождения переменной ОбъектExcel = Неопределено; и небольшого цикла попыток удаления с использованием блока Попытка. Это гарантирует стабильную работу системы даже при высокой нагрузке на дисковую подсистему сервера.

← На главную