Как передать параметры типа Enum в COM-объект и обойти ошибку «Несоответствие типов» в 1С на примере CorelDraw

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

При интеграции 1С с внешними приложениями через механизм COM/OLE (например, продуктами Microsoft Office или графическими редакторами вроде CorelDraw) разработчики часто сталкиваются со специфическими ошибками приведения типов. Одна из самых распространенных и коварных проблем — ошибка «Несоответствие типов» при вызове методов, параметры которых в родной среде (VBA, C++) описаны как перечисления (Enum) или являются необязательными (Optional).

В этой статье мы подробно разберем, почему возникает эта ошибка, как устроены перечисления Enum с точки зрения платформы 1С, и как правильно вызывать строгие COM-методы на примере функции выравнивания объектов в CorelDraw.

В чем суть проблемы: теория взаимодействия 1С и COM

Рассмотрим классическую ситуацию. В VBA код выравнивания объектов выглядит просто и лаконично:


CorelApp.ActiveDocument.Pages(1).Layers(2).Shapes(6).AlignAndDistribute(cdrAlignDistributeHDistributeCenter, cdrAlignDistributeVDistributeCenter, cdrAlignShapesToCenterOfPage)

Здесь cdrAlignDistributeHDistributeCenter — это именованная константа типа Enum. Внутри библиотеки типов (Type Library) внешнего приложения под каждым элементом такого перечисления скрывается обычное целое число (обычно типа Integer или Long).

Когда мы пытаемся выполнить аналогичное действие в 1С, мы сталкиваемся с двумя препятствиями:

  1. Отсутствие прямого доступа к константам: Платформа 1С использует позднее связывание (Late Binding) через интерфейс IDispatch. Она не может напрямую прочитать список констант из библиотеки типов, поэтому конструкции вида CorelApp.cdrAlignDistributeHDistributeCenter вызывают ошибку обращения к свойству.
  2. Ошибка приведения типов при передаче чисел: Казалось бы, логично заменить константы их числовыми аналогами (например, заменить cdrAlignDistributeHNone на 0, а cdrAlignDistributeHAlignRight на 1). Однако при вызове метода вроде shp.AlignAndDistribute(3, 0, 2) 1С упорно возвращает ошибку «Несоответствие типов».

Давайте разберем по шагам, как решить эту задачу и заставить внешнее приложение правильно принимать параметры из 1С.

Шаг 1. Выясняем числовые значения перечислений Enum

Прежде всего, нам необходимо узнать, какие именно числа скрываются за красивыми именами констант в VBA. Сделать это можно тремя путями:

Шаг 2. Разгадка ошибки «Несоответствие типов» — необязательные параметры

Проанализируем ситуацию, в которой простая подстановка чисел (например, shp.AlignAndDistribute(3, 0, 2)) все равно вызывает сбой. Проблема здесь кроется вовсе не в числах.

Существуют два типа COM-серверов по степени строгости обработки параметров:

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

Шаг 3. Передача сложных типов данных и пустых объектов

Посмотрим на полную сигнатуру метода AlignAndDistribute в CorelDraw:


AlignAndDistribute(
    AlignH As cdrAlignDistributeH, 
    AlignV As cdrAlignDistributeV, 
    Placement As cdrAlignShapesTo, 
    TargetArea As cdrAlignDistributeH, 
    RelativeToPage As Boolean, 
    Reserved1 As Long, 
    Reserved2 As Long, 
    Reserved3 As Long, 
    TargetRect As Rect
)

Как мы видим, метод принимает 9 параметров. Последний параметр — это объект типа Rect (прямоугольник границ выравнивания). Из-за него стандартная подстановка простых типов терпит неудачу:

Единственный верный способ передать этот параметр в 1С — физически создать пустой экземпляр нужного COM-объекта и передать его в метод. В нашем случае это класс CorelDraw.Rect.

Результирующий рабочий код вызова метода будет выглядеть следующим образом:


// Создаем необходимый вспомогательный объект-пустышку
RectClass = Новый COMОбъект("CorelDraw.Rect");

// Передаем ВСЕ параметры метода, соблюдая типы данных
// 1-й параметр (3) - HAlignCenter
// 2-й параметр (0) - VAlignNone
// 3-й параметр (2) - AlignToPage
// ...последний параметр - созданный нами объект RectClass
shp.AlignAndDistribute(3, 0, 2, 0, Ложь, 2, 0, 0, RectClass);

Как сделать код читаемым (избавляемся от магических чисел)

Писать в коде вызовы с набором непонятных цифр — плохая практика программирования. Чтобы сделать код документированным и понятным для других разработчиков, мы рекомендуем использовать структуру-обертку, имитирующую системное перечисление.

Рассмотрим пример красивой реализации:


// Объявляем структуру аналогов Enum для CorelDraw
cdrAlignH = Новый Структура;
cdrAlignH.Вставить("None", 0);
cdrAlignH.Вставить("AlignRight", 1);
cdrAlignH.Вставить("AlignLeft", 2);
cdrAlignH.Вставить("AlignCenter", 3);

cdrAlignV = Новый Структура;
cdrAlignV.Вставить("None", 0);
cdrAlignV.Вставить("AlignTop", 1);
cdrAlignV.Вставить("AlignBottom", 2);
cdrAlignV.Вставить("AlignCenter", 3);

// Вызов метода с использованием структуры становится понятным и читаемым
RectClass = Новый COMОбъект("CorelDraw.Rect");
shp.AlignAndDistribute(
    cdrAlignH.AlignCenter, 
    cdrAlignV.None, 
    2, 
    0, 
    Ложь, 
    2, 
    0, 
    0, 
    RectClass
);

Важное замечание по оптимизации производительности

Разберем критически важный нюанс, связанный со скоростью работы программы. Операция создания нового COM-объекта через конструктор Новый COMОбъект(...) является крайне «дорогой» и ресурсоемкой для операционной системы. При ее вызове происходит обращение к реестру Windows, инициализация сторонней библиотеки в памяти и выделение системных дескрипторов.

Если вы обрабатываете большое количество объектов (например, перебираете сотни шейпов на страницах CorelDraw в цикле), категорически запрещено создавать вспомогательный объект внутри итераций цикла.

Посмотрим на пример правильной организации пакетной обработки объектов:


CorelApp = Новый COMОбъект("CorelDraw.Application");
CorelApp.Visible = True;

// ПРАВИЛЬНО: Создаем пустой Rect-объект ОДИН раз перед началом циклов
RectClass = Новый COMОбъект("CorelDraw.Rect");

Для каждого pg Из CorelApp.ActiveDocument.Pages Цикл
    Для каждого lyr Из pg.Layers Цикл
        Для каждого shp Из lyr.Shapes Цикл
            
            Если shp.Name = "Название наряда" Тогда
                shp.Text.Frame.Range.Text = "Новое значение текста";
                
                // Переиспользуем один и тот же объект RectClass на каждой итерации
                shp.AlignAndDistribute(3, 0, 2, 0, Ложь, 2, 0, 0, RectClass);
            КонецЕсли;    
            
        КонецЦикла;
    КонецЦикла;
КонецЦикла;

Вынос инициализации CorelDraw.Rect за пределы циклов позволяет ускорить выполнение данного алгоритма в десятки раз и предотвратит утечки оперативной памяти на сервере или клиентской машине.

← На главную