Как в C# через COM перебрать структуру и соответствие 1С без изменения конфигурации?

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

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

Разберем детально механизм взаимодействия .NET и платформы 1С, выясним, почему возникает это ограничение и как правильно организовать обход коллекций 1С на стороне C# без доработки конфигурации или изменения исходного кода обработок.

Причина проблемы: особенности интерфейсов COM и встроенного языка

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

При обращении через OLE/COM из внешней среды кажется, что без знания имен ключей прочитать данные невозможно. Но в платформе 1С:Предприятие для объектов-коллекций реализован стандартный COM-интерфейс IEnumVARIANT (через DISPID_NEWENUM). Именно этот интерфейс обеспечивает работу цикла Для Каждого внутри 1С и позволяет внешним языкам перебирать элементы.

Способ 1: Прямой перебор через цикл foreach в C#

Поскольку среда выполнения .NET CLR автоматически проецирует интерфейс IEnumVARIANT COM-объекта в стандартный интерфейс System.Collections.IEnumerable, нам не требуется вручную вызывать низкоуровневые методы навигации. Достаточно использовать стандартный оператор foreach языка C#.

Посмотрим на пример обхода структуры или соответствия 1С в C# с использованием типа dynamic:


// Подключаемся к базе через COMConnector
Type comType = Type.GetTypeFromProgID("V83.COMConnector");
dynamic connector = Activator.CreateInstance(comType);
dynamic connection = connector.Connect(@"File=""C:\Bases\Demo"";Usr=""Admin"";");

// Получаем объект обработки или результат вычислений
dynamic extProcessing = connection.ВнешниеОбработки.Создать(@"C:\Shared\GetData.epf");
dynamic resultData = extProcessing.ПолучитьРезультат(); // Возвращает Структуру или Соответствие

// Перебираем элементы коллекции
foreach (dynamic item in (System.Collections.IEnumerable)resultData)
{
    // Каждый элемент представляет собой объект 1С "КлючИЗначение"
    dynamic key = item.Key;      // или item.Ключ
    dynamic value = item.Value;  // или item.Значение

    Console.WriteLine($"Ключ: {connection.String(key)}, Значение: {connection.String(value)}");
}

Рассмотрим подробнее, что происходит внутри цикла:

  1. Приведение объекта к System.Collections.IEnumerable запрашивает у COM-объекта интерфейс перечислителя.
  2. На каждом шаге итерации платформа 1С возвращает объект системного типа КлючИЗначение.
  3. У полученного элемента доступны свойства Ключ (или Key) и Значение (или Value), которые содержат исходные данные.

Способ 2: Определение типов данных 1С из внешнего кода

Когда ключом или значением соответствия является не примитивный тип, а ссылка, массив или вложенная структура, необходимо корректно определить тип полученного объекта. Встроенная функция ТипЗнч() недоступна напрямую через внешнее соединение, так как является синтаксической конструкцией языка 1С. Для решения этой задачи используем системные методы глобального контекста:


// Получение строкового представления объекта 1С
string stringRepresentation = connection.String(value);

// Получение типа XDTO для точного анализа типа 1С
dynamic xdtoType = connection.XMLТипЗнч(value);
if (xdtoType != null)
{
    string typeName = xdtoType.TypeName; // Имя типа (например, "CatalogRef.Контрагенты")
    Console.WriteLine($"Тип данных 1С: {typeName}");
}

Способ 3: Сериализация сложных структур на стороне 1С в JSON или XML

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

Разберем по шагам сериализацию через СериализаторXDTO или объект ЗаписьJSON:


// Создаем объект ЗаписьJSON в контексте 1С
dynamic jsonWriter = connection.NewObject("ЗаписьJSON");
jsonWriter.УстановитьСтроку();

// Записываем структуру данных
connection.ЗаписатьJSON(jsonWriter, resultData);

// Получаем готовую JSON-строку
string jsonString = jsonWriter.Закрыть();

// Теперь строку можно разобрать любым стандартным парсером .NET (System.Text.Json или Newtonsoft.Json)
Console.WriteLine(jsonString);

Этот подход избавляет от необходимости рекурсивно обходить дерево COM-объектов и существенно повышает скорость передачи больших объемов данных.

Важные правила работы с COM-объектами 1С в C#

При реализации обхода коллекций 1С через COM-соединение обязательно учитываем следующие архитектурные особенности:

  1. Управление памятью: Сборщик мусора .NET (Garbage Collector) не управляет временем жизни COM-объектов 1С. При интенсивном переборе коллекций необходимо явно освобождать COM-ссылки вызовом Marshal.ReleaseComObject(item) или Marshal.FinalReleaseComObject(connection) после завершения работы, чтобы предотвратить утечки памяти и зависание сеансов на сервере 1С.
  2. Разрядность процессов: Разрядность приложения на C# (x86 или x64) должна строго совпадать с разрядностью установленного COM-коннектора comcntr.dll платформы 1С. При несовпадении потребуется использовать суррогатный процесс через службу компонентов Windows (COM+).
  3. Область видимости динамического кода: Конструкция Выполнить() выполняет код в изолированной области видимости — локальные переменные внутри блока уничтожаются сразу после выполнения команды. Поэтому динамический запуск кода не подходит для сохранения промежуточных ссылок на ключи.
← На главную