Работа с внешними источниками данных через COM-соединение в 1С часто преподносит сюрпризы начинающим разработчикам. Одной из самых распространенных проблем является получение данных, которые не относятся к примитивным типам (числам, строкам, датам). При попытке обратиться к реквизиту типа Перечисление или СправочникСсылка напрямую, система возвращает объект типа COMОбъект, с которым невозможно работать привычными методами. Разберем подробно, почему это происходит и как правильно обрабатывать такие данные.
При использовании COM-соединения (будь то V83.Application или V83.COMConnector) между основной и вызываемой базой «нативно» передаются только простые типы. Как только мы пытаемся получить ссылку на объект (например, вид операции документа), вызываемая база передает указатель на этот объект в своей памяти. Основная база не знает структуры этого объекта, поэтому интерпретирует его как неопознанный COMОбъект.
Проанализируем типичный пример. Если мы пишем код:
Сообщить("Вид операции: " + Выборка.ВидОперации);
Система выдаст ошибку или просто напишет "COMОбъект", так как она не может автоматически преобразовать ссылку из чужой базы в понятную строку. Рассмотрим способы решения этой задачи.
Если нам нужно просто вывести название перечисления или реквизита для информативности, самым простым и надежным методом будет использование функции XMLСтрока на стороне вызываемой базы. Этот метод преобразует ссылку в её внутреннее строковое представление, которое легко передается через границы COM.
Разберем, как это реализовать в коде:
// Предположим, ДругаяБаза — это наше COM-соединение
// Выборка — это результат метода Выбрать()
Сообщить("Вид операции: " + ДругаяБаза.XMLСтрока(Выборка.ВидОперации));
Этот метод хорош тем, что он универсален и работает как для перечислений, так и для ссылок на справочники или документы. Мы получаем текстовое имя (для перечислений) или Уникальный Идентификатор (для ссылок), который является строкой и корректно отображается в текущем сеансе.
Часто возникает задача не просто показать значение, а выполнить проверку. Например: «Если вид операции равен ПолучениеЗайма, тогда выполнять действие». Прямое сравнение Выборка.ВидОперации = Перечисления.ВидыОпераций.ПолучениеЗайма не сработает, так как слева у нас COMОбъект из внешней базы, а справа — ссылка из текущей базы.
Чтобы сравнение прошло успешно, необходимо обращаться к метаданным внешней базы. Посмотрим на правильный синтаксис:
Если Выборка.ВидОперации = ДругаяБаза.Перечисления.ВидыОперацийПоступлениеДенежныхСредств.ПолучениеЗайма Тогда
// Логика обработки
КонецЕсли;
В данном случае мы сравниваем два объекта, принадлежащих одному и тому же COM-контенсту, и 1С корректно распознает их идентичность. Также можно использовать индекс значения перечисления через метод Порядок(), если названия типов в базах совпадают, но это менее надежный путь.
Выборка объектов через метод Выбрать() и последующий обход в цикле — это самый медленный способ работы через COM. Каждый вызов Следующий() или обращение к реквизиту Выборка.Сумма инициирует сетевой вызов между процессами, что крайне ресурсозатратно.
Рассмотрим, как «правильные пацаны» (как выразились участники обсуждения) решают эту задачу через объект Запрос. Весь фильтр по перечислениям и организациям переносится на сторону внешней базы, а в основную базу возвращаются уже готовые примитивные данные.
Пошаговый пример реализации запроса через COM:
Запрос = ДругаяБаза.NewObject("Запрос");
Запрос.Текст =
"ВЫБРАТЬ
| Поступление.СуммаДокумента КАК Сумма,
| Поступление.Дата КАК Дата,
| Поступление.Номер КАК Номер
|ИЗ
| Документ.ПоступлениеНаРасчетныйСчет КАК Поступление
|ГДЕ
| Поступление.Дата МЕЖДУ &НачДата И &КонДата
| И Поступление.Проведен
| И Поступление.ВидОперации = ЗНАЧЕНИЕ(Перечисление.ВидыОперацийПоступлениеДенежныхСредств.ПолучениеЗайма)";
Запрос.УстановитьПараметр("НачДата", НачДата);
Запрос.УстановитьПараметр("КонДата", КонецДня(КонДата));
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
Сообщить("Документ №" + Выборка.Номер + " от " + Выборка.Дата + " на сумму " + Выборка.Сумма);
КонецЦикла;
Преимущества этого метода:
COMОбъект, так как фильтр ЗНАЧЕНИЕ(...) отрабатывает внутри запроса.В дискуссии упоминалось использование V83.Application. Важно понимать разницу:
V83.Application — запускает полноценный экземпляр 1С с графическим интерфейсом. Это медленно, требует больше памяти и часто вызывает появление лишних окон на панели задач.V83.COMConnector — предназначен специально для программного обмена данными. Он не загружает графическую оболочку, работает быстрее и «незаметнее».Если вы все же используете Application, не забывайте правильно завершать сеанс, чтобы в диспетчере задач не оставалось «зависших» процессов. Проанализируем правильный выход из соединения:
ДругаяБаза.Visible = Ложь; // Скрываем окно, если оно вдруг появилось
ДругаяБаза.ЗавершитьРаботуСистемы(Ложь); // Завершаем работу без вопросов
ДругаяБаза = Неопределено; // Очищаем переменную для освобождения памяти
Один из важных вопросов: можно ли из отчета в первой базе «провалиться» (открыть форму) в документ второй базы? Напрямую это сделать невозможно. Ссылка Выборка.Ссылка, полученная через COM, не является валидной ссылкой для текущей информационной базы. При попытке передать её в ОткрытьФорму() возникнет ошибка преобразования данных XDTO.
Выясним, как обойти это ограничение. Если базы открыты на одном компьютере, можно использовать механизм Навигационных ссылок. Вы получаете навигационную ссылку документа во второй базе (через ПолучитьНавигационнуюСсылку()), передаете её строкой в первую базу, и при клике вызываете команду системы:
ЗапуститьПриложение("e1cib/data/Документ.ПоступлениеНаРасчетныйСчет?ref=...");
Это позволит пользователю мгновенно переключиться на нужный документ в уже открытом окне второй базы.
Для эффективной работы через COM-соединение придерживайтесь следующих правил:
во-первых, всегда отдавайте предпочтение COMConnector вместо Application;
во-вторых, старайтесь выполнять всю тяжелую работу (фильтрацию, поиск) внутри запроса на стороне подключаемой базы;
в-третьих, передавайте в основную базу только примитивные типы (числа, строки, даты), используя при необходимости XMLСтрока или УникальныйИдентификатор() для ссылочных типов.