Как исправить ошибку «Invalid class string» (0x800401F3) при загрузке данных из Excel на сервере 1С?

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

Разработка механизмов интеграции и загрузки данных из внешних источников — одна из самых частых задач для программиста 1С. При этом разработчики нередко сталкиваются с ситуацией, когда обработка загрузки данных из файлов Microsoft Excel идеально работает на локальном компьютере (файловая копия базы), но при попытке запустить её в клиент-серверном режиме на рабочей базе выдает ошибку:

«Ошибка при вызове конструктора (ComОбъект) Ex=Новый ComОбъект("Excel.Application") ... reason: -2147221005(0x800401F3): Invalid class string»

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

Почему возникает ошибка «Invalid class string» на сервере 1С?

Для начала проанализируем техническую суть проблемы. Когда код выполняется в процедуре с директивой &НаСервере, конструктор Новый COMОбъект("Excel.Application") пытается инициализировать приложение Microsoft Excel непосредственно на стороне сервера приложений «1С:Предприятие». Ошибка инициализации COM-класса в этом случае происходит по одной из следующих причин:

  1. Отсутствие установленного Microsoft Excel на сервере. Программа 1С не может создать COM-объект, если соответствующий программный пакет физически не установлен на сервере приложений.
  2. Конфликт разрядности (битности) систем. Если служба сервера «1С:Предприятие» работает в 64-разрядном режиме (x64), а на сервере установлен 32-разрядный (x86) Microsoft Office, операционная система Windows не сможет сопоставить вызовы. 64-битный рабочий процесс 1С (rphost.exe) не увидит зарегистрированный в 32-битной ветке реестра класс Excel.
  3. Ограничения прав учетной записи службы 1С. Агент сервера 1С чаще всего запускается под специальной локальной учетной записью (например, USR1CV8) или под системной записью LocalSystem. У этих пользователей по умолчанию отсутствуют права на запуск и активацию интерактивных DCOM-приложений, к которым относится Microsoft Excel.
  4. Отсутствие папок системного профиля (Desktop). Для инициализации некоторых COM-компонентов Office требуется физическое наличие папки Рабочего стола в профиле пользователя, под которым работает служба. Если ее нет, Excel аварийно завершает работу при старте.

Решение 1. Перенос логики чтения файла на сторону клиента

Самый простой и архитектурно верный способ избежать проблем с правами на сервере — выполнять чтение файла непосредственно на компьютере пользователя (в контексте &НаКлиенте), где Excel гарантированно установлен и работает под текущим сеансом Windows. Однако здесь разработчики часто совершают другую ошибку: они пытаются прочитать COM-объект на клиенте и передать полученный объект листа Worksheet в серверную процедуру для записи данных.

Попытка передать COM-объект напрямую на сервер приведет к ошибке передачи данных XDTO («Data transfer between client and server error. Invalid value type...»), поскольку COM-объекты не могут быть сериализованы и переданы между контекстами клиента и сервера.

Правильный подход заключается в следующем: мы инициализируем Excel на клиенте, считываем данные в простую структуру (например, в массив структур или таблицу значений) и уже этот безопасный массив передаем на сервер. Давайте разберем этот алгоритм на конкретном примере кода:


&НаКлиенте
Процедура ВыполнитьЗагрузку(Команда)
    
    Диалог = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
    Диалог.Фильтр = "Файлы Excel (*.xls;*.xlsx)|*.xls;*.xlsx";
    Диалог.Заголовок = "Выберите файл для загрузки";
    
    Если Диалог.Выбрать() Тогда
        ПутьКФайлу = Диалог.ПолноеИмяФайла;
        
        // Шаг 1. Читаем данные на клиенте в простой массив структур
        МассивДанных = ПрочитатьФайлExcelНаКлиенте(ПутьКФайлу);
        
        Если МассивДанных.Количество() > 0 Тогда
            // Шаг 2. Передаем массив на сервер для обработки
            СоздатьНоменклатуруНаСервере(МассивДанных);
            ПоказатьПредупреждение(, "Загрузка успешно завершена!");
        Иначе
            ПоказатьПредупреждение(, "Файл пуст или не содержит корректных данных.");
        КонецЕсли;
    КонецЕсли;
    
КонецПроцедуры

&НаКлиенте
Функция ПрочитатьФайлExcelНаКлиенте(Знач ПутьКФайлу)
    
    МассивРезультатов = Новый Массив;
    
    Попытка
        ExcelПриложение = Новый COMОбъект("Excel.Application");
    Исключение
        ВызватьИсключение "Не удалось запустить Microsoft Excel на локальном компьютере. Возможно, приложение не установлено.";
    КонецПопытки;
    
    ExcelПриложение.DisplayAlerts = Ложь;
    
    Попытка
        Книга = ExcelПриложение.Workbooks.Open(ПутьКФайлу);
        Лист = Книга.Sheets(1); // Работаем с первым листом
        
        // Определяем заполненные строки
        ВсегоСтрок = Лист.UsedRange.Rows.Count;
        
        // Начинаем считывание со второй строки (пропускаем шапку таблицы)
        Для НомерСтроки = 2 По ВсегоСтрок Цикл
            
            КодАртикула = СокрЛП(Лист.Cells(НомерСтроки, 1).Value);
            Наименование = СокрЛП(Лист.Cells(НомерСтроки, 2).Value);
            
            Если ПустаяСтрока(Наименование) Тогда
                Продолжить; // Пропускаем пустые строки
            КонецЕсли;
            
            // Записываем данные в простую структуру
            СтрокаДанных = Новый Структура;
            СтрокаДанных.Вставить("Артикул", КодАртикула);
            СтрокаДанных.Вставить("Наименование", Наименование);
            
            МассивРезультатов.Добавить(СтрокаДанных);
        КонецДля;
        
        Книга.Close(Ложь);
    Исключение
        ExcelПриложение.Quit();
        ВызватьИсключение "Ошибка при чтении файла Excel: " + ОписаниеОшибки();
    КонецПопытки;
    
    ExcelПриложение.Quit();
    Возврат МассивРезультатов;
    
КонецФункции

&НаСервере
Процедура СоздатьНоменклатуруНаСервере(МассивДанных)
    
    НачатьТранзакцию();
    
    Попытка
        Для Каждого СтрокаДанных Из МассивДанных Цикл
            
            // Проверяем, существует ли уже номенклатура с таким артикулом
            НайденнаяСсылка = Справочники.Номенклатура.НайтиПоРеквизиту("Артикул", СтрокаДанных.Артикул);
            
            Если НайденнаяСсылка.Пустая() Тогда
                НовыйОбъект = Справочники.Номенклатура.СоздатьЭлемент();
                НовыйОбъект.Наименование = СтрокаДанных.Наименование;
                НовыйОбъект.Артикул = СтрокаДанных.Артикул;
                // Заполняем другие необходимые реквизиты по умолчанию
                НовыйОбъект.Записать();
            КонецЕсли;
            
        КонецДля;
        
        ЗафиксироватьТранзакцию();
    Исключение
        ОтменитьТранзакцию();
        ЗаписатьЛогСобытия("ОшибкаЗагрузки", ОписаниеОшибки(), УровеньЖурналаРегистрации.Ошибка);
        ВызватьИсключение "Ошибка при записи номенклатуры на сервере. Операция отменена.";
    КонецПопытки;
    
КонецПроцедуры

Решение 2. Настройка прав DCOM и профиля на сервере

Если специфика вашей задачи требует запуска загрузки в неинтерактивном режиме (например, в регламентном задании, которое выполняется строго на сервере по расписанию), вам придется настроить операционную систему сервера Windows для работы с COM-объектом Excel. Разберем по шагам, как это сделать:

  1. Зайдите на сервер под учетной записью Администратора.
  2. Откройте службу «Службы компонентов» (команда dcomcnfg.exe через строку выполнения Windows). Если у вас 64-разрядная ОС, а MS Office установлен 32-разрядный, запустите консоль в 32-битном режиме командой: mmc comexp.msc /32.
  3. В дереве консоли перейдите: Службы компонентов -> Компьютеры -> Мой компьютер -> Настройка DCOM.
  4. Найдите в списке приложение Microsoft Excel Application, нажмите правой кнопкой мыши и выберите «Свойства».
  5. На вкладке «Безопасность» найдите группу «Права на запуск и активацию», установите флаг «Настроить» и нажмите кнопку «Изменить». Добавьте пользователя, под которым запускается служба 1С (например, локального пользователя USR1CV8), и предоставьте ему полные права («Локальный запуск», «Локальная активация»).
  6. На вкладке «Удостоверение» убедитесь, что запуск настроен от имени «Запускающего пользователя» (Launching user) или жестко пропишите данные административной учетной записи.
  7. Создайте папки рабочего стола в профиле системы. Это критически важное условие для инициализации MS Office в неинтерактивных сессиях служб Windows. Создайте пустые папки Desktop по следующим путям (в зависимости от разрядности Windows):
    • Для 64-битных систем: C:\Windows\SysWOW64\config\systemprofile\Desktop
    • Для 32-битных систем: C:\Windows\System32\config\systemprofile\Desktop
  8. Дайте права полного доступа к созданным папкам Desktop для пользователя службы сервера 1С.

Решение 3. Чтение файлов штатными средствами 1С (Рекомендуемый метод)

Использование COM-объектов постепенно уходит в прошлое. Этот метод медленный, нестабильный (процессы Excel часто «зависают» в памяти сервера, блокируя файлы) и абсолютно не кроссплатформенный (код не будет работать, если сервер 1С запущен на ОС Linux). Начиная с версии платформы 8.3.6, компания 1С добавила возможность прямого программного чтения файлов форматов .xls, .xlsx и .ods без использования стороннего ПО.

Для реализации этого метода мы передаем файл на сервер через временное хранилище, а затем на сервере считываем его напрямую в объект ТабличныйДокумент. Разберем, как реализовать этот современный подход:


&НаКлиенте
Процедура ЗагрузитьФайлШтатнымиСредствами(Команда)
    
    // Выбор файла пользователем на клиенте
    ПараметрыВыбора = Новый ПараметрыДиалогаПомещенияФайла;
    ПараметрыВыбора.Фильтр = "Таблицы Excel (*.xlsx; *.xls)|*.xlsx;*.xls";
    ПараметрыВыбора.Заголовок = "Выберите файл для загрузки номенклатуры";
    
    ОписаниеФайла = Новый ОписаниеПередаваемогоФайла;
    
    НачатьПомещениеФайлаНаСервер(
        Новый ОписаниеОповещения("ПослеПомещенияФайла", ЭтотОбъект),
        ,,,
        ПараметрыВыбора,
        УникальныйИдентификатор
    );
    
КонецПроцедуры

&НаКлиенте
Процедура ПослеПомещенияФайла(Результат, АдресИлиРезультат, ВыбранноеИмяФайла, ДополнительныеПараметры) Экспорт
    
    // Если пользователь успешно выбрал и передал файл на сервер
    Если АдресИлиРезультат <> Неопределено Тогда
        // Вызываем серверный метод для парсинга и загрузки
        ЗагрузитьИзХранилищаНаСервере(АдресИлиРезультат);
        ПоказатьПредупреждение(, "Загрузка номенклатуры успешно выполнена!");
    КонецЕсли;
    
КонецПроцедуры

&НаСервере
Процедура ЗагрузитьИзХранилищаНаСервере(Знач АдресХранилища)
    
    // Создаем имя для временного файла на сервере
    ИмяВременногоФайла = ПолучитьИмяВременногоФайла("xlsx");
    
    // Получаем двоичные данные файла из хранилища и записываем на диск сервера
    ДвоичныеДанныеФайла = ПолучитьИзВременногоХранилища(АдресХранилища);
    ДвоичныеДанныеФайла.Записать(ИмяВременногоФайла);
    
    // Инициализируем ТабличныйДокумент 1С
    ТабДок = Новый ТабличныйДокумент;
    
    Попытка
        // Читаем файл Excel напрямую без использования COM-объектов!
        ТабДок.Прочитать(ИмяВременногоФайла, СпособЧтенияЗначенийТабличногоДокумента.Значение);
    Исключение
        УдалитьФайлы(ИмяВременногоФайла);
        ВызватьИсключение "Не удалось прочитать файл на сервере. Ошибка: " + ОписаниеОшибки();
    КонецПопытки;
    
    // После чтения обязательно удаляем временный файл с диска сервера
    УдалитьФайлы(ИмяВременногоФайла);
    
    // Преобразуем ТабличныйДокумент в стандартную ТаблицуЗначений с помощью ПостроителяЗапроса
    Построитель = Новый ПостроительЗапроса;
    Построитель.ИсточникДанных = Новый ОписаниеИсточникаДанных(ТабДок.Область());
    Построитель.ЗаполнитьНастройки();
    Построитель.Выполнить();
    
    ТаблицаДанных = Построитель.Результат.Выгрузить();
    
    // Обрабатываем полученную таблицу
    НачатьТранзакцию();
    Попытка
        Для Каждого СтрокаТаблицы Из ТаблицаДанных Цикл
            
            // Предположим, что колонки в Excel называются "Артикул" и "Наименование"
            // В ТаблицеДанных имена колонок могут быть автоматически названы как "Ф1", "Ф2" и т.д.
            // Проверим заполненность первой колонки (индекс 0)
            Артикул = СокрЛП(СтрокаТаблицы[0]);
            Наименование = СокрЛП(СтрокаТаблицы[1]);
            
            Если ПустаяСтрока(Наименование) Тогда
                Продолжить;
            КонецЕсли;
            
            НайденнаяСсылка = Справочники.Номенклатура.НайтиПоРеквизиту("Артикул", Артикул);
            
            Если НайденнаяСсылка.Пустая() Тогда
                НоваяНоменклатура = Справочники.Номенклатура.СоздатьЭлемент();
                НоваяНоменклатура.Наименование = Наименование;
                НоваяНоменклатура.Артикул = Артикул;
                НоваяНоменклатура.Записать();
            КонецЕсли;
            
        КонецДля;
        ЗафиксироватьТранзакцию();
    Исключение
        ОтменитьТранзакцию();
        ВызватьИсключение "Ошибка транзакции при записи данных: " + ОписаниеОшибки();
    КонецПопытки;
    
КонецПроцедуры

Использование встроенного метода чтения файлов через ТабличныйДокумент решает проблему раз и навсегда. Программа больше не зависит от установленных версий Microsoft Office на сервере приложений, скорость чтения возрастает в несколько раз, а код становится стабильным и готовым к работе на любых современных серверах 1С под управлением Windows или Linux.

← На главную