При интеграции двух информационных баз 1С через COMConnector разработчики часто сталкиваются с неочевидной проблемой: файл, который должен быть записан на удаленном сервере (базе-приемнике), почему-то оказывается на диске сервера-источника. В этой статье мы подробно разберем, почему так происходит, изучим механизмы контекстов исполнения и реализуем корректную схему передачи данных.
Рассмотрим типовую ситуацию. Программист создает объект V83.COMConnector, подключается к удаленной базе и пытается вызвать метод Записать() для объекта ДвоичныеДанные. На первый взгляд кажется, что раз объект получен из «внешней базы», то и команды его методов должны выполняться там. Однако это заблуждение.
Когда мы работаем через COM, создается сессия с типом Внешнее соединение. Код, который мы пишем в базе-источнике, обращаясь к объектам ВнешняяБаза, фактически выполняется в потоке клиентского процесса (или серверного процесса базы-источника), который инициировал соединение. Система интерпретирует путь к файлу относительно того компьютера, на котором запущен сам код управления COM-объектом.
Проанализируем пример кода, который часто приводит к ошибке:
// Код в базе-источнике
COM1с = Новый COMObject("V83.COMConnector");
ВнешняяБаза = COM1с.Connect(СтрокаСоединения);
ДвоичныеДанныеНаСервере = ВнешняяБаза.NewObject("ДвоичныеДанные", ПутьКФайлуИсточнику);
// Файл будет записан на сервере ИСТОЧНИКЕ, а не ПРИЕМНИКЕ
ДвоичныеДанныеНаСервере.Записать("C:\Exchange\file.xml");
Чтобы решить эту задачу, нам необходимо принудительно перенести выполнение кода из контекста внешнего соединения в контекст сервера приложений базы-приемника.
Для того чтобы команда записи выполнилась физически на дисках удаленного сервера, нам нужно создать специальную «прослойку» в базе-приемнике. Разберем по шагам, как это реализовать.
Шаг 1. Настройка общих модулей в базе-приемнике
Нам потребуется два общих модуля (или один с комбинированными свойствами, но лучше разделить логику). Главное условие — наличие флага Вызов сервера. Без этого флага выполнение не покинет пределы потока внешнего соединения.
хаз_ОбменКонтрагентами. Установим флаги: Сервер, Внешнее соединение.хаз_ОбменКонтрагентамиВызовСервера. Установим флаги: Сервер, Вызов сервера.Шаг 2. Реализация кода в базе-приемнике
В модуле хаз_ОбменКонтрагентами пропишем экспортную процедуру, которую будем вызывать извне. Она будет служить входной точкой:
// Общий модуль: хаз_ОбменКонтрагентами (Сервер, Внешнее соединение)
Процедура СохранитьФайлНаСервере(ДанныеФайлаВВидеДвоичныхДанных, ИмяФайлаПриемника) Экспорт
// Здесь мы все еще в контексте внешнего соединения.
// Переходим в истинный серверный контекст через модуль с "Вызовом сервера"
хаз_ОбменКонтрагентамиВызовСервера.ЗаписатьДанныеНаДиск(ДанныеФайлаВВидеДвоичныхДанных, ИмяФайлаПриемника);
КонецПроцедуры
В модуле хаз_ОбменКонтрагентамиВызовСервера реализуем непосредственную запись:
// Общий модуль: хаз_ОбменКонтрагентамиВызовСервера (Сервер, Вызов сервера)
Процедура ЗаписатьДанныеНаДиск(ДвоичныеДанные, ПолныйПуть) Экспорт
// Выполнение этой строки произойдет строго на сервере приложений базы-приемника
ДвоичныеДанные.Записать(ПолныйПуть);
КонецПроцедуры
Шаг 3. Вызов из базы-источника
Теперь в базе-источнике мы вызываем нашу серверную процедуру. Важно передать файл в виде объекта ДвоичныеДанные, чтобы платформа сама сериализовала его при передаче через границу COM-соединения.
// Код в базе-источнике
ДвоичныеДанныеДляОтправки = Новый ДвоичныеДанные(ПутьКЛокальномуФайлу);
// Вызываем метод удаленной базы
ВнешняяБаза.хаз_ОбменКонтрагентами.СохранитьФайлНаСервере(ДвоичныеДанныеДляОтправки, "D:\Data\In\Message.xml");
Рассмотрим ситуацию с функцией ПолучитьИмяВременногоФайла(). Выясним причину путаницы: если вы вызываете ВнешняяБаза.ПолучитьИмяВременногоФайла(), платформа вернет путь, который является временным с точки зрения вашего текущего сеанса (источника), но сформирован по правилам удаленной системы. Это не гарантирует, что запись по этому пути «пробросится» на удаленный сервер.
Проанализируем эксперимент из обсуждения:
Метод Записать() всегда производит запись в контексте процесса, владеющего объектом. Если объект ДвоичныеДанные живет в памяти процесса COMConnector на стороне сервера «А», он будет пытаться найти путь C:\Temp именно на сервере «А», даже если путь был получен от сервера «Б».
Единственный надежный способ — передача объекта данных внутрь процедуры, которая помечена как Сервер и Вызов сервера в целевой базе.
Даже при идеально написанном коде файл может не сохраниться. Разберем основные причины:
rphost (обычно это USR1CV8). У этого пользователя должны быть полные права (чтение/запись) на целевую папку.\\ServerName\Share\File.xml. Убедитесь, что служба 1С имеет доступ к этой сетевой шаре.COMConnector зарегистрирован корректно. Иногда требуется настройка в dcomcnfg (Службы компонентов), чтобы COM-объект запускался в собственной памяти (библиотека v83.comconnector должна работать в составе приложения COM+).Если файлы имеют значительный объем (сотни мегабайт), передача через параметры COM-методов может быть нестабильной. В таких случаях рассмотрим алгоритм через временное хранилище:
ДвоичныеДанные.ПоместитьВоВременноеХранилище(Данные).ФоновыеЗадания.Выполнить(), передавая адрес хранилища.Фоновое задание в 1С всегда выполняется в контексте сервера, что гарантирует сохранение файла в файловой системе сервера приложений без каких-либо исключений, связанных с типом соединения.
Использование описанных выше методик позволит вам четко контролировать местоположение файлов и избежать типичных ошибок, связанных с контекстной зависимостью платформы 1С:Предприятие при работе через COM-интерфейс.