COM-объект живёт на клиенте, данные нужны на сервере: рабочие схемы для 1С 8.3

· 9 мин

Содержание

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

Типичная ситуация: на клиенте открыли Excel через Excel.Application, хотите отдать лист в процедуру &НаСервере для записи в справочник — и получаете ошибку передачи параметра. Решение зависит от того, где установлена COM-программа: у пользователя, на сервере или нигде.

Как устроены клиент и сервер 1С и что можно передать между ними

В управляемом приложении код формы выполняется в двух местах. Процедуры с &НаКлиенте работают в процессе 1cv8c.exe (тонкий клиент) или 1cv8.exe (толстый клиент) на компьютере пользователя. Процедуры с &НаСервере и &НаСервереБезКонтекста выполняются в рабочем процессе кластера rphost.exe — часто на другой машине.

Каждый серверный вызов — это пересылка параметров по сети. Платформа упаковывает значения, передаёт их и распаковывает на другой стороне. Поэтому через границу проходят только сериализуемые типы.

Можно передатьНельзя передать
✅ Число, Строка, Дата, Булево, Неопределено❌ COMОбъект
✅ Массив, Структура, Соответствие из простых значений❌ COMSafeArray
✅ Ссылки на объекты базы (СправочникСсылка...)❌ Объекты внешних компонент
✅ ДвоичныеДанные, УникальныйИдентификатор❌ Значения, которые содержат хотя бы один из типов выше
✅ Адрес во временном хранилище (строка)❌ Открытые файлы и потоки клиента

Структура, в которую положили хотя бы одно COM-значение, тоже не передастся. Проверяйте не только сам параметр, но и всё, что лежит внутри.

Отдельно про файловый вариант базы. Там «сервер» работает внутри того же клиентского процесса, но правила контекстов действуют так же. Код, который передаёт COM-объект на сервер, упадёт и в файловой базе — платформа проверяет типы параметров независимо от режима.

Почему COM-объект не передаётся на сервер напрямую

COM-объект — это не данные, а указатель на объект в памяти конкретного процесса Windows. Excel, Word или другая COM-программа запущены на машине пользователя. Сервер 1С физически не может обратиться к этой памяти.

Причин, по которым платформа запрещает такую передачу, несколько:

  • 🔌 Нет сериализации. COM-объект нельзя превратить в поток байтов и восстановить на другой стороне — он живёт, пока жив процесс, который его создал.
  • 🖥️ Разные машины. Клиент — рабочая станция пользователя, сервер — отдельный компьютер. Ссылка на объект в памяти одного не имеет смысла на другом.
  • 🐧 Сервер может работать под Linux. Там технологии COM нет вообще, и переносимого кода с COM на сервере не получится.
  • 🌐 Веб-клиент. В браузере COM практически недоступен, а серверная часть одна для всех клиентов.

Отсюда вывод: вопрос «как передать COM-объект» всегда превращается в вопрос «где создать COM-объект и что из него передать». Рабочих схем три: читать на клиенте, отдавать файл или создавать объект на сервере.

Схема 1. Прочитать данные на клиенте и передать на сервер простые значения

Самый надёжный вариант, когда COM-программа установлена только у пользователя. Клиент открывает файл, читает нужные поля в массив структур, закрывает COM-объект и одним вызовом отдаёт массив на сервер.

&НаКлиенте
Процедура ЗагрузитьИзExcel(Команда)

	Данные = Новый Массив;
	Excel = Неопределено;

	Попытка
		Excel = Новый COMОбъект("Excel.Application");
		Excel.DisplayAlerts = Ложь;
		Книга = Excel.Workbooks.Open(ПутьКФайлу);
		Лист = Книга.Worksheets(1);
		ВсегоСтрок = Лист.UsedRange.Rows.Count;

		Для НомерСтроки = 2 По ВсегоСтрок Цикл
			Строка = Новый Структура("Артикул, Количество, Цена");
			Строка.Артикул = СокрЛП(Лист.Cells(НомерСтроки, 1).Value);
			Строка.Количество = Лист.Cells(НомерСтроки, 2).Value;
			Строка.Цена = Лист.Cells(НомерСтроки, 3).Value;
			Данные.Добавить(Строка);
		КонецЦикла;

		Книга.Close(Ложь);
	Исключение
		Сообщить("Не удалось прочитать файл: " + ОписаниеОшибки());
	КонецПопытки;

	Если Excel <> Неопределено Тогда
		Excel.Quit();
		Excel = Неопределено;
	КонецЕсли;

	Если Данные.Количество() > 0 Тогда
		ЗаписатьНаСервере(Данные);
	КонецЕсли;

КонецПроцедуры

&НаСервереБезКонтекста
Процедура ЗаписатьНаСервере(Данные)

	Для Каждого Строка Из Данные Цикл
		// Поиск номенклатуры по Строка.Артикул и запись в базу
	КонецЦикла;

КонецПроцедуры

Что здесь важно:

  1. Чтение и закрытие — на клиенте. COM-объект не покидает клиентскую процедуру. На сервер уходит Массив из Структура с примитивами — он сериализуется без проблем.
  2. Значения ячеек уже приведены к типам 1С. Свойство Value возвращает Число, Строку или Дату, а не COM-обёртку. Их можно класть в структуру как есть.
  3. Один серверный вызов на весь файл. Не вызывайте сервер в цикле по строкам: 5 000 строк — это 5 000 сетевых обращений вместо одного.
  4. Excel.Quit() в любом случае. Иначе на компьютере пользователя останется висеть процесс EXCEL.EXE.

Для ВсегоСтрок используется UsedRange.Rows.Count — это верно, если данные начинаются с первой строки листа. Если над таблицей есть пустые строки, берите номер последней строки как UsedRange.Row + UsedRange.Rows.Count - 1.

Обращение к каждой ячейке — отдельный COM-вызов, и на больших листах это медленно. Быстрее прочитать весь диапазон одним обращением: Лист.UsedRange.Value вернёт COMSafeArray. Но его тоже нельзя передать на сервер — сначала выгрузите его в обычный массив методом Выгрузить() и уже массив передавайте.

Если COM-объект нужен на клиенте между несколькими вызовами, храните его в переменной модуля формы, объявленной с &НаКлиенте. В реквизит формы COM-объект положить нельзя: реквизиты хранятся на сервере.

Схема 2. Передать на сервер файл, а не COM-объект

Часто COM на клиенте нужен только для того, чтобы прочитать файл. Тогда проще отдать серверу сам файл: двоичные данные сериализуются, а разбирать их можно уже там.

&НаКлиенте
Процедура ОтправитьФайл(Команда)
	ДД = Новый ДвоичныеДанные(ПутьКФайлу);
	Адрес = ПоместитьВоВременноеХранилище(ДД, УникальныйИдентификатор);
	ОбработатьФайлНаСервере(Адрес);
КонецПроцедуры

&НаСервереБезКонтекста
Процедура ОбработатьФайлНаСервере(Адрес)
	ДД = ПолучитьИзВременногоХранилища(Адрес);
	ИмяФайла = ПолучитьИмяВременногоФайла("xlsx");
	ДД.Записать(ИмяФайла);

	ТабДок = Новый ТабличныйДокумент;
	ТабДок.Прочитать(ИмяФайла);
	// Разбор ТабДок.Область(...) построчно

	УдалитьФайлы(ИмяФайла);
КонецПроцедуры

Плюсы этого пути:

  • 📄 ТабличныйДокумент.Прочитать() читает xlsx без Excel вообще — ни на клиенте, ни на сервере ничего ставить не нужно.
  • 🐧 Код работает и на сервере под Linux.
  • 🌐 Подходит для веб-клиента: там файл выбирают через диалог и отправляют асинхронными методами помещения файла, а не через конструктор ДвоичныеДанные.

Временный файл на сервере удаляйте после разбора, в том числе при ошибке. Иначе каталог временных файлов рабочего процесса разрастается до гигабайтов.

Если файл большой (сотни мегабайт), временное хранилище нагружает память рабочего процесса. Такие файлы лучше класть в общий сетевой каталог и передавать на сервер только путь к нему — строкой.

Схема 3. Создать COM-объект прямо на сервере 1С

Когда COM-компонента нужна для серверной логики — регламентное задание, фоновая обработка, интеграция с внешней системой, — объект создают на сервере. Синтаксис тот же:

&НаСервереБезКонтекста
Функция ПолучитьДанныеЧерезCOM(ИмяФайла)
	Объект = Новый COMОбъект("Excel.Application");
	// ...работа с объектом...
	Объект.Quit();
	Объект = Неопределено;
КонецФункции

Код короткий, но сервер под него нужно подготовить. Проверьте по порядку:

  1. Сервер 1С работает под Windows. Под Linux конструктор COMОбъект на сервере недоступен.
  2. Компонента установлена и зарегистрирована на сервере, а не на компьютере программиста. ProgID ищется в реестре той машины, где запущен rphost.exe.
  3. Совпадает разрядность. 64-битный сервер 1С не загрузит 32-битную внутрипроцессную компоненту (DLL) — будет ошибка «класс не зарегистрирован». Внепроцессные серверы вроде Excel работают при любой разрядности.
  4. У учётной записи службы 1С есть права. Служба агента обычно запущена от отдельного пользователя (например, USR1CV8). Права на запуск и активацию COM-приложения настраиваются в оснастке dcomcnfg → Службы компонентов → Компьютеры → Мой компьютер → Настройка DCOM.
  5. Для Excel и Word — каталог Desktop. Office, запущенный из службы без профиля, часто не открывает файлы и возвращает пустой объект. Помогает создание пустых папок C:\Windows\System32\config\systemprofile\Desktop и C:\Windows\SysWOW64\config\systemprofile\Desktop.

Для подключения к другой базе 1С на сервере используют V83.COMConnector. Его библиотека comcntr.dll лежит в каталоге bin платформы и регистрируется командой от администратора:

regsvr32 "C:\Program Files\1cv8\8.3.24.1548\bin\comcntr.dll"

Номер версии в пути подставьте свой — он должен совпадать с версией и разрядностью сервера 1С.

Microsoft официально не поддерживает автоматизацию Office из служб и серверных процессов. Excel на сервере зависает на модальных окнах, плодит процессы и плохо переносит параллельную работу нескольких пользователей. Для чтения таблиц на сервере выбирайте схему 2.

Зависшие процессы EXCEL.EXE на сервере, запущенные от учётной записи службы 1С, можно завершить через диспетчер задач. Перед этим убедитесь, что сейчас не идёт загрузка у пользователей: их операция оборвётся с ошибкой. Безопаснее делать это вне рабочего времени.

Ошибки при работе с COM и как их перехватывать

Любую работу с COM оборачивайте в Попытка ... Исключение. COM-программа живёт своей жизнью: файл может быть занят, лист переименован, сама программа не установлена. Без перехвата пользователь увидит системное окно с кодом и потеряет данные формы.

Минимальный шаблон для серверной стороны:

Попытка
	Объект = Новый COMОбъект("Excel.Application");
Исключение
	Инфо = ИнформацияОбОшибке();
	ЗаписьЖурналаРегистрации("Загрузка.COM", УровеньЖурналаРегистрации.Ошибка,,,
		ПодробноеПредставлениеОшибки(Инфо));
	ВызватьИсключение "Не удалось запустить Excel на сервере. Обратитесь к администратору.";
КонецПопытки;

Подробности уходят в журнал регистрации, пользователь видит понятный текст. Администратор потом найдёт запись по событию Загрузка.COM в Администрирование → Обслуживание → Журнал регистрации.

Диагностика: выберите симптом

Чем заменить COM-взаимодействие

COM — старая технология с жёсткой привязкой к Windows. Для большинства задач в 1С 8.3 есть способы без неё, и они работают одинаково на клиенте и на сервере.

ЗадачаВместо COMГде работает
Прочитать или сохранить xlsxТабличныйДокумент.Прочитать() / Записать()Клиент и сервер, Windows и Linux
Обмен с другой базой 1СHTTP-сервис, веб-сервис, план обменаСервер
Обмен с внешней программойHTTP-запрос (HTTPСоединение), JSON, XMLКлиент и сервер
Работа с внешней СУБДВнешние источники данныхСервер
Оборудование и нестандартные функцииВнешняя компонента по технологии Native APIКлиент и сервер, в том числе Linux
Разбор CSV и текстовых выгрузокЧтениеТекста, СтрРазделить()Клиент и сервер

Внешние компоненты Native API — главная замена COM для своих библиотек. Компоненту подключают методом ПодключитьВнешнююКомпоненту(), она может работать и на сервере, если собрана под нужную операционную систему и разрядность. Объект внешней компоненты, как и COM-объект, между клиентом и сервером не передаётся: его подключают там, где он нужен.

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

Частые вопросы

Можно ли передать COM-объект на сервер через временное хранилище?
Нет. Временное хранилище принимает только сериализуемые значения. Поместить туда COM-объект с клиента не получится — нужно положить прочитанные из него данные или двоичные данные файла.
Работает ли COM на сервере 1С под Linux?
Нет. COM — технология Windows. На Linux-сервере используйте внешние компоненты Native API, HTTP-сервисы или чтение файлов средствами платформы.
Почему в файловой базе COM в серверной процедуре работает, а в клиент-серверной — нет?
В файловом варианте серверный код выполняется на компьютере пользователя, где компонента зарегистрирована. В клиент-серверном варианте код уходит на сервер, а там компоненты нет или у службы не хватает прав.
Можно ли хранить COM-объект в реквизите формы?
Нет. Реквизиты формы живут на сервере и передаются при каждом вызове. Держите объект в переменной модуля формы с директивой &НаКлиенте.
Как ускорить чтение большого листа Excel на клиенте?
Читайте диапазон целиком через свойство Value и выгружайте полученный COMSafeArray в массив, вместо обращения к каждой ячейке. Сервер вызывайте один раз с готовым массивом.