Как выполнить код 1С из внешней компоненты, созданной по технологии Native API?

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

При разработке внешних компонент (ВК) для 1С на C++ часто возникает задача не только вызывать функции компоненты из 1С, но и инициировать выполнение кода на стороне 1С из самой компоненты. Нередко это приводит к путанице, особенно у программистов, не имеющих глубокого опыта в C++. Разберем типичную ошибку и правильные подходы к решению этой задачи.

Проанализируем ситуацию: разработчик создал внешнюю компоненту по технологии Native API и пытается из нее выполнить код 1С, используя методы, характерные для COM-объектов, такие как GetPropertyByName и Invoke1. Это приводит к ошибкам, так как интерфейс, передаваемый в Native-компоненту, имеет совершенно другую природу.


// Пример ошибочного кода в Native-компоненте
#include "Component.h"
#include 

// IDispatch *m_iConnect; // Неверный тип для Native API
// CComPtr m_iConnect; // Также неверно

bool Component::Init(void *connection_) 
{
    // connection_ здесь - это указатель на IAddInDefBase, а не на IDispatch
    // Попытка привести его к IDispatch и использовать COM-методы обречена на провал
    // m_iConnect = ?????? как инициализировать?
    return connection_ != nullptr;
}

bool Component::CallAsFunc(...) 
{
    // Этот код предназначен для COM-компоненты и не будет работать в Native ВК
    hr = m_iConnect.GetPropertyByName(L"AppDispatch", ...); 
    // ...
    hr = pApp.Invoke1(L"New", &CComVariant(L"ТаблицаЗначений"), ...);
}

Выясним причину этой ошибки и рассмотрим корректные способы взаимодействия.

В чем заключается основная ошибка: путаница Native API и COM

Ключевая проблема заключается в смешении двух совершенно разных технологий создания внешних компонент для 1С. Понимание их различий — основа для правильного проектирования взаимодействия.

1. Технология Native API

Это современный и рекомендуемый способ создания ВК. Компонента представляет собой нативную динамическую библиотеку (.dll в Windows, .so в Linux), которая загружается непосредственно в процесс 1С. Взаимодействие с платформой происходит через стандартизированный интерфейс IAddInDefBase.

2. Технология COM (Component Object Model)

Это классическая технология Microsoft, работающая только в среде Windows. Компонента регистрируется в системе как COM-сервер.

Посмотрим на сигнатуру метода Init для COM-компоненты:


STDMETHODIMP CAddInCOM::Init(IDispatch *pConnection) 
{
    if (!pConnection) return E_FAIL;
    m_iConnect = pConnection; // m_iConnect имеет тип IDispatch* или CComPtr
    m_iConnect->AddRef();
    return S_OK;
}

Таким образом, попытка использовать код для COM-взаимодействия внутри Native-компоненты неверна в своей основе. Не существует простого способа "превратить" указатель IAddInDefBase* в IDispatch*. Это разные сущности. Теперь рассмотрим, как же правильно решать задачу для Native-компонент.

Решение 1: Использование механизма внешних событий (ExternalEvent)

Это основной и штатный способ для Native-компоненты инициировать выполнение кода на стороне 1С. Механизм работает асинхронно и идеально подходит для уведомления 1С о каких-либо событиях, произошедших в компоненте (например, получены данные от оборудования, завершился фоновый расчет).

Разберем по шагам:

  1. На стороне C++ (внешняя компонента):

    Внутри метода компоненты, где нужно инициировать действие в 1С, вызовите метод ExternalEvent у интерфейса IAddInDefBase. Этот интерфейс вы получили и сохранили на этапе вызова Init.

    
    // В классе компоненты
    IAddInDefBase *connection;
    
    // ... в методе Init
    bool Component::Init(void *connection_) {
        connection = static_cast(connection_);
        return connection != nullptr;
    }
    
    // ... в методе, который должен вызвать код 1С
    void Component::DoSomethingAndNotify1C() {
        // ... какая-то работа ...
    
        // Генерируем внешнее событие
        // 1-й параметр: Имя источника (уникальное имя вашей компоненты)
        // 2-й параметр: Имя события (команда, которую нужно выполнить)
        // 3-й параметр: Данные (строка с параметрами, можно использовать разделители или JSON)
        const WCHAR_T* source = L"MySuperComponent";
        const WCHAR_T* event = L"DataReady";
        const WCHAR_T* data = L"{\"file_id\": 123, \"status\": \"success\"}";
    
        if (connection) {
            connection->ExternalEvent(source, event, data);
        }
    }
    
  2. На стороне 1С (модуль формы):

    В модуле формы, где используется компонента, необходимо создать процедуру-обработчик ОбработкаВнешнегоСобытия.

    
    &НаКлиенте
    Процедура ОбработкаВнешнегоСобытия(Источник, Событие, Данные)
            
        Если Источник = "MySuperComponent" Тогда
                
            Если Событие = "DataReady" Тогда
                // Здесь выполняется нужный код 1С
                // Можно разобрать строку 'Данные', если она передана в формате JSON
                Попытка
                    ЧтениеJSON = Новый ЧтениеJSON;
                    ЧтениеJSON.УстановитьСтроку(Данные);
                    СтруктураДанных = ПрочитатьJSON(ЧтениеJSON);
                        
                    Сообщить("ВК сообщила о готовности данных для файла: " + СтруктураДанных.file_id);
                    // Вызов других процедур и функций 1С
                    ОбработатьГотовыеДанные(СтруктураДанных);
                        
                Исключение
                    Сообщить("Не удалось разобрать данные от ВК: " + ОписаниеОшибки());
                КонецПопытки;
            КонецЕсли;
                
        КонецЕсли;
            
    КонецПроцедуры
    

Важный момент: вызов ExternalEvent является асинхронным. Код в C++ не будет ждать, пока выполнится обработчик в 1С, и не сможет напрямую получить от него результат. Этот способ предназначен для уведомлений, а не для вызова функций с возвратом значения.

Решение 2: Использование HTTP-сервисов

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

Схема работы:

  1. На стороне 1С: Создайте и опубликуйте на веб-сервере HTTP-сервис с методами, которые будут выполнять нужный код. Например, метод для создания и заполнения какого-либо документа.
  2. На стороне C++ (внешняя компонента): Используйте любую C++ библиотеку для работы с HTTP (например, cURL, cpp-httplib, POCO). Компонента формирует HTTP-запрос (GET, POST), передавая параметры в URL или теле запроса (часто в формате JSON), и отправляет его на эндпоинт вашего сервиса.
  3. Результат: 1С обрабатывает запрос, выполняет код и возвращает результат в теле HTTP-ответа, который ваша компонента может прочитать и обработать.

Преимущества этого подхода:

В заключение, важно четко понимать, какую технологию создания ВК вы используете. Если это Native API, забудьте про AppDispatch и COM-методы. Ваш основной инструмент для инициирования действий в 1С — это ExternalEvent. Если же требуется сложная логика с синхронным обменом данными, смотрите в сторону HTTP-сервисов.

← На главную