В практике разработки на 1С часто встречается ситуация, когда код, который долгое время работал без сбоев, внезапно начинает выдавать ошибку. Одна из таких «плавающих» проблем — отказ функции ПолучитьCOMОбъект(ИмяФайла) при работе с файлами Microsoft Excel. Давайте разберемся, почему это происходит и какие существуют более надежные способы для решения этой задачи.
Проблема обычно выглядит так: строка кода ДокExcel = ПолучитьCOMОбъект(ИмяФайла);, которая раньше успешно открывала файл и возвращала COM-объект, начинает вызывать исключение «Ошибка при вызове метода контекста (ПолучитьCOMОбъект)». При этом подключение к уже запущенному экземпляру Excel через ПолучитьCOMОбъект(, "Excel.Application") может продолжать работать.
Причины такого поведения чаще всего кроются не в коде 1С, а во внешнем окружении:
EXCEL.EXE от предыдущих сеансов работы, которые блокируют создание нового объекта.Вместо того чтобы искать причину в окружении, что бывает долго и не всегда результативно, рассмотрим два надежных способа, которые помогут решить проблему.
Этот подход является более стабильным и управляемым, чем ПолучитьCOMОбъект. Мы не полагаемся на систему в поиске или создании приложения для файла, а явно создаем новый экземпляр Excel и даем ему команду открыть нужный файл. Это дает больше контроля над процессом.
Посмотрим на пример. Изначально код, который перестал работать, мог выглядеть так:
// Старый, нестабильно работающий вариант
Попытка
Книга = ПолучитьCOMОбъект(inputFileName);
Ексель = Книга.Application;
Ексель.DisplayAlerts = Ложь;
Лист = Книга.Sheets(1);
Исключение
// ... обработка ошибки
КонецПопытки;
Заменим его на более надежную конструкцию. Разберем по шагам:
Новый COMОбъект("Excel.Application").Workbooks.Open(ИмяФайла).Вот как выглядит итоговый код:
&НаСервере
Процедура ПрочитатьФайлЧерезНовыйCOM()
ИмяФайла = "C:\Temp\МойФайл.xlsx"; // Укажите свой путь к файлу
Excel = Неопределено;
Попытка
// 1. Создаем новый объект приложения Excel
Excel = Новый COMОбъект("Excel.Application");
// Управляем видимостью окна и оповещениями (полезно для фоновой обработки)
Excel.Visible = Ложь;
Excel.DisplayAlerts = Ложь;
// 2. Открываем книгу в созданном приложении
Книга = Excel.Workbooks.Open(ИмяФайла);
// 3. Получаем доступ к листу (например, к первому)
Лист = Книга.Worksheets(1);
// ... здесь ваш код для работы с данными листа ...
Сообщить("Файл успешно открыт. Значение ячейки А1: " + Лист.Cells(1,1).Value);
Исключение
Предупреждение("Внимание! Файл не открыт. " + ОписаниеОшибки());
// Если Excel был создан, но произошла ошибка при открытии файла,
// его все равно нужно попытаться закрыть
Если Excel <> Неопределено Тогда
Excel.Quit();
КонецЕсли;
Возврат;
КонецПопытки;
// 4. Корректно завершаем работу с COM-объектами
Если Excel <> Неопределено Тогда
// Закрываем книгу без сохранения изменений
Книга.Close(Ложь);
// Закрываем приложение Excel
Excel.Quit();
// Освобождаем переменные
Excel = Неопределено;
КонецЕсли;
КонецПроцедуры
Обратите внимание: при работе на сервере даже этот метод может выдать ошибку «Невозможно получить свойство Open класса Workbooks». Чаще всего это связано с тем, что у системного пользователя, от имени которого запущена служба сервера 1С, нет папки Desktop в его системном профиле. Для решения может потребоваться создать каталоги C:\Windows\SysWOW64\config\systemprofile\Desktop и C:\Windows\System32\config\systemprofile\Desktop.
Это самый надежный способ для чтения данных из файлов .xls и .xlsx, особенно при выполнении кода на сервере. Он полностью исключает взаимодействие с COM, а значит, ему не нужен установленный MS Excel на машине, где выполняется код. Этот метод не зависит от прав DCOM, обновлений Office и других внешних факторов.
Преимущества:
Ограничения:
Рассмотрим пример функции, которая читает данные с указанного листа Excel и возвращает их в виде таблицы значений.
&НаСервере
Функция ПрочитатьЛистExcelНаСервере(пИмяФайла, пИмяЛиста)
ТабличныйДокумент = Новый ТабличныйДокумент;
// Считываем весь файл в объект ТабличныйДокумент
Попытка
ТабличныйДокумент.Прочитать(пИмяФайла, СпособЧтенияЗначенийТабличногоДокумента.Значение);
Исключение
Сообщить("Не удалось прочитать файл: " + пИмяФайла);
Возврат Новый ТаблицаЗначений;
КонецПопытки;
// Преобразуем имя листа в имя области
// Это ключевой момент! Имена областей формируются по особым правилам.
ИмяОбласти = "_" + СтрЗаменить(пИмяЛиста, ".", "_");
ИмяОбласти = СтрЗаменить(ИмяОбласти, ",", "_");
ИмяОбласти = СтрЗаменить(ИмяОбласти, " ", "_");
ИмяОбласти = СтрЗаменить(ИмяОбласти, "(", "_");
ИмяОбласти = СтрЗаменить(ИмяОбласти, ")", "_");
// и т.д. для всех служебных символов
// Пытаемся найти область по имени
Если ТабличныйДокумент.Области.Найти(ИмяОбласти) = Неопределено Тогда
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "В файле не найден лист с именем """ + пИмяЛиста + """ (проверено имя области """ + ИмяОбласти + """).";
Сообщение.Сообщить();
Возврат Новый ТаблицаЗначений;
КонецЕсли;
// Используем ПостроительЗапроса для удобной выгрузки данных из области в ТаблицуЗначений
ПостроительЗапроса = Новый ПостроительЗапроса;
ПостроительЗапроса.ИсточникДанных = Новый ОписаниеИсточникаДанных(ТабличныйДокумент.Область(ИмяОбласти));
ПостроительЗапроса.Выполнить();
РезультатТЗ = ПостроительЗапроса.Результат.Выгрузить();
Возврат РезультатТЗ;
КонецФункции
Очень важный нюанс, который нужно учесть, — это формирование имени области. Когда 1С считывает файл Excel, имена листов преобразуются в имена именованных областей в объекте ТабличныйДокумент по следующим правилам:
_._.Например:
Вызов такой функции будет выглядеть просто:
&НаКлиенте
Процедура ПримерВызоваЧтения()
ИмяФайла = "C:\Temp\МойФайл.xlsx";
ИмяЛиста = "01 Контрагенты";
ТаблицаДанных = ПрочитатьЛистExcelНаСервере(ИмяФайла, ИмяЛиста);
Если ТаблицаДанных.Количество() > 0 Тогда
Сообщить("Данные с листа '" + ИмяЛиста + "' успешно загружены!");
// Дальнейшая работа с таблицей значений...
КонецЕсли;
КонецПроцедуры
Таким образом, если вы столкнулись с нестабильной работой ПолучитьCOMОбъект, рекомендуем перейти на один из предложенных методов. Для универсальных задач и работы с форматированием используйте Новый COMОбъект, а для надежного чтения данных на сервере — встроенный объект ТабличныйДокумент.