При переносе базы 1С из файлового варианта в клиент-серверный режим работы (под управлением Microsoft SQL Server или PostgreSQL) разработчики часто сталкиваются с ошибкой при чтении файлов Microsoft Excel. Код, который штатно отрабатывал на рабочей станции пользователя, в серверном контексте аварийно завершается с ошибкой -2147467262 (0x80004002): Интерфейс не поддерживается.
Проанализируем причины возникновения этой проблемы и разберем по шагам варианты ее устранения — от правильной настройки серверного окружения Windows до перехода на современные встроенные механизмы платформы 1С:Предприятие, не требующие использования COM-технологий.
Выясним причину, почему в файловой базе или на клиенте код работает, а на сервере падает в исключение. При выполнении кода на стороне сервера инициализация COM-объекта происходит в контексте изолированной неинтерактивной службы (Session 0) под учетной записью пользователя службы сервера 1С (обычно usr1cv8). Это накладывает ряд системных ограничений:
0x80004002 напрямую указывает на сбой маршалинга COM-интерфейсов из-за конфликта веток реестра.Если отказаться от использования Excel.Application на сервере невозможно, выполним настройку операционной системы Windows Server.
C:\Windows\SysWOW64\config\systemprofile\Desktop
C:\Windows\System32\config\systemprofile\Desktop
usr1cv8).
taskkill /im excel.exe /f
Откроем оснастку Службы компонентов (dcomcnfg.exe), перейдем по дереву: Службы компонентов → Компьютеры → Мой компьютер → Настройка DCOM.
regedit и найдем ветку HKEY_CLASSES_ROOT\TypeLib\{00020813-0000-0000-C000-000000000046}.
1.7, 1.8, 1.9), удалим ключи, соответствующие старым, не установленным в данный момент версиям Office.Наиболее надежным и платформенно-независимым решением является полный отказ от COM-объектов в пользу встроенного объекта платформы ТабличныйДокумент. Этот метод работает на сервере без установки Microsoft Office, корректно функционирует в базах под управлением Linux и не блокирует память серверов зависшими процессами excel.exe.
Рассмотрим пример кода для серверного чтения файлов формата .xlsx и .xls:
&НаСервере
Процедура ПрочитатьФайлExcelЧерезТабличныйДокумент(ПолноеИмяФайла)
ТабДок = Новый ТабличныйДокумент;
Попытка
// Читаем файл формата xlsx или xls напрямую средствами платформы
ТабДок.Прочитать(ПолноеИмяФайла, СпособЧтенияЗначенийТабличногоДокумента.Значение);
Исключение
ВызватьИсключение "Не удалось прочитать файл: " + КраткоеПредставлениеОшибки(ИнформацияОбОшибке());
КонецПопытки;
ВсегоСтрок = ТабДок.ВысотаТаблицы;
ВсегоКолонок = ТабДок.ШиринаТаблицы;
// Обходим строки полученного документа (начиная со 2-й, если 1-я — заголовок)
Для НомерСтроки = 2 По ВсегоСтрок Цикл
Артикул = ТабДок.ПолучитьОбласть(НомерСтроки, 1).Текст;
НоменклатураНаименование = ТабДок.ПолучитьОбласть(НомерСтроки, 2).Текст;
Количество = ТабДок.ПолучитьОбласть(НомерСтроки, 3).ТекущаяОбласть.Значение;
Цена = ТабДок.ПолучитьОбласть(НомерСтроки, 4).ТекущаяОбласть.Значение;
Если ПустаяСтрока(Артикул) И ПустаяСтрока(НоменклатураНаименование) Тогда
Продолжить; // Пропускаем пустые строки
КонецЕсли;
// Обработка полученных данных и заполнение реквизитов или табличных частей
КонецЦикла;
КонецПроцедуры
Если конфигурация жестко завязана на логику работы с макросами, диаграммами или специфическими COM-свойствами Excel, выполнение создания объекта следует вынести в клиентский контекст под управление интерактивной сессии пользователя. Прочитанные данные затем передаются на сервер через структуры или таблицы значений.
Посмотрим на пример организации взаимодействия клиента и сервера:
&НаКлиенте
Процедура ЗагрузитьФайлНаКлиенте(ПутьКФайлу)
Попытка
ExcelПриложение = Новый COMОбъект("Excel.Application");
ExcelПриложение.Visible = Ложь;
ExcelПриложение.DisplayAlerts = Ложь;
Исключение
Сообщить("Не удалось запустить Excel на клиенте: " + ОписаниеОшибки());
Возврат;
КонецПопытки;
Книга = ExcelПриложение.Workbooks.Open(ПутьКФайлу);
Лист = Книга.Sheets(1);
МассивДанных = Новый Массив;
НомерСтроки = 2;
Пока ЗначениеЗаполнено(Лист.Cells(НомерСтроки, 1).Value) Цикл
СтрокаДанных = Новый Структура("Код, Наименование, Цена");
СтрокаДанных.Код = Лист.Cells(НомерСтроки, 1).Value;
СтрокаДанных.Наименование = Лист.Cells(НомерСтроки, 2).Value;
СтрокаДанных.Цена = Лист.Cells(НомерСтроки, 3).Value;
МассивДанных.Добавить(СтрокаДанных);
НомерСтроки = НомерСтроки + 1;
КонецЦикла;
Книга.Close(Ложь);
ExcelПриложение.Quit();
// Передаем собранные данные для записи в базу на сервер
ЗагрузитьДанныеВДокументНаСервере(МассивДанных);
КонецПроцедуры
&НаСервере
Процедура ЗагрузитьДанныеВДокументНаСервере(МассивДанных)
// Заполнение табличной части документа и запись объекта базы
КонецПроцедуры
Если требуется высокая скорость обработки объемных файлов на сервере без создания ресурсоемкого процесса excel.exe, можно использовать подключение через ADODB. Для этого на сервере устанавливается компонент Microsoft Access Database Engine.
Разберем пример подключения к файлу как к источнику реляционных данных:
&НаСервере
Функция ПрочитатьExcelЧерезADO(ПутьКФайлу, ИмяЛиста = "Лист1")
ТаблицаРезультат = Новый ТаблицаЗначений;
Connection = Новый COMОбъект("ADODB.Connection");
СтрокаПодключения = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + ПутьКФайлу + ";Extended Properties=""Excel 12.0 XML;HDR=YES;IMEX=1"";";
Попытка
Connection.Open(СтрокаПодключения);
Recordset = Новый COMОбъект("ADODB.Recordset");
ТекстЗапроса = "SELECT * FROM [" + ИмяЛиста + "$]";
Recordset.Open(ТекстЗапроса, Connection, 0, 1); // adOpenForwardOnly, adLockReadOnly
// Создаем колонки в ТаблицеЗначений на основе полей Recordset
Для НомерКолонки = 0 По Recordset.Fields.Count - 1 Цикл
ТаблицаРезультат.Колонки.Добавить(Recordset.Fields(НомерКолонки).Name);
КонецЦикла;
Пока НЕ Recordset.EOF Цикл
НоваяСтрока = ТаблицаРезультат.Добавить();
Для НомерКолонки = 0 По Recordset.Fields.Count - 1 Цикл
НоваяСтрока[НомерКолонки] = Recordset.Fields(НомерКолонки).Value;
КонецЦикла;
Recordset.MoveNext();
КонецЦикла;
Recordset.Close();
Connection.Close();
Исключение
Сообщить("Ошибка чтения через ADO: " + ОписаниеОшибки());
КонецПопытки;
Возврат ТаблицаРезультат;
КонецФункции
Итог: Использование ТабличныйДокумент.Прочитать() — самый надежный и простой способ интеграции, решающий проблему несовместимости прав и архитектурных ограничений серверного вызова COM-объектов.