При разработке внешних компонент (DLL) часто возникает задача «обратной связи»: когда не 1С вызывает метод компоненты, а сама компонента должна инициировать выполнение какого-либо алгоритма в 1С. Это необходимо для работы с торговым оборудованием (сканеры штрихкодов, весы), при получении данных из сокетов или при завершении длительных фоновых вычислений в библиотеке. В данной статье мы подробно разберем, как заставить 1С выполнить ваш код, используя интерфейсы COM и Native API.
Это самый правильный и «штатный» способ взаимодействия, который используется в 1С для работы с торговым оборудованием. Суть метода заключается в том, что компонента генерирует событие, которое попадает в очередь 1С и обрабатывается в специальной предопределенной процедуре модуля.
Для реализации этого механизма в вашей ВК (написанной на C++) необходимо воспользоваться интерфейсом IAsyncEvent. Рассмотрим, как это выглядит на практике. Сначала мы получаем указатель на интерфейс при инициализации компоненты, а затем вызываем метод ExternalEvent.
// Пример на C++
if (pIAsyncEvent) {
// Устанавливаем глубину буфера событий
pIAsyncEvent->SetEventBufferDepth(2);
// Генерируем событие: Источник, Сообщение, Данные
pIAsyncEvent->ExternalEvent(L"MyComponentSource", L"DataReceived", L"Значение_из_DLL");
}
Проанализируем параметры метода ExternalEvent:
Теперь посмотрим на сторону 1С. Чтобы поймать это событие, в форме или в модуле обычного приложения (или в модуле управляемого приложения) должна быть определена процедура ОбработкаВнешнегоСобытия. Разберем пример кода на языке 1С:
Процедура ОбработкаВнешнегоСобытия(Источник, Сообщение, Данные)
Если Источник = "MyComponentSource" Тогда
Если Сообщение = "DataReceived" Тогда
Сообщить("Получены данные из DLL: " + Данные);
// Здесь можно вызвать любую процедуру 1С
ВыполнитьНужноеДействие(Данные);
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Важное замечание: Главное преимущество этого способа — потокобезопасность. Вы можете вызывать ExternalEvent из любого фонового потока вашей DLL. 1С сама поставит событие в очередь и выполнит его в основном интерфейсном потоке, когда будет готова.
Если вам недостаточно просто отправить событие и вы хотите напрямую вызывать методы глобального контекста 1С или создавать объекты 1С (например, Запрос или ТаблицаЗначений) прямо из кода библиотеки, вам понадобится свойство AppDispatch.
При подключении компоненты через метод Init, платформа передает указатель pConnection (типа IDispatch). Из этого указателя можно извлечь контекст приложения. Рассмотрим, как это реализовать на Delphi:
// Сохраняем переданный коннект
var
PConnect: IDispatch;
AppDispatch: OleVariant;
// В процедуре Init сохраняем указатель
PConnect := pConnection;
// Позже, например в CallAsFunc, получаем доступ к приложению
AppDispatch := OleVariant(PConnect).AppDispatch;
Имея AppDispatch, мы можем обращаться к 1С так же, как если бы мы работали через COM-соединение. Выясним, какие возможности это открывает:
// Создание объекта 1С прямо из DLL
var
Запрос: OleVariant;
begin
Запрос := AppDispatch.NewObject('Запрос');
Запрос.Текст := 'ВЫБРАТЬ ПЕРВЫЕ 1 Наименование ИЗ Справочник.Номенклатура';
// ... выполнение запроса
end;
Также вы можете вызвать экспортную функцию из общего модуля 1С. Для этого в 1С создайте общий модуль (например, ВзаимодействиеСВК) и отметьте галки «Глобальный» и «Сервер/Клиент». Напишите там вспомогательную функцию для выполнения произвольного кода:
// В общем модуле 1С
Функция ИсполнитьКодИзВК(ТекстКода) Экспорт
Попытка
Выполнить(ТекстКода);
Возврат "ОК";
Исключение
Возврат ОписаниеОшибки();
КонецПопытки;
КонецФункции
Теперь из кода DLL вы сможете вызвать эту функцию через AppDispatch.ИсполнитьКодИзВК("Сообщить('Привет из DLL!')"). Это дает практически безграничную власть над системой, но требует осторожности.
Проанализируем ситуацию, когда компонента работает на сервере в многопользовательском режиме. Здесь возникают нюансы, которые могут привести к нестабильности:
AppDispatch на сервере может «не видеть» некоторые методы, доступные только на клиенте (например, методы работы с формами).IsMultiThread := True;.Рассмотрим подробнее, какой метод лучше подходит под ваши задачи:
Внешние события (ExternalEvent):
Прямой вызов (AppDispatch):
OleVariant, вызывать любые функции и сразу получать результат.Посмотрим на практический совет: если ваша задача — просто уведомить 1С о событии (например, «карта считана» или «файл загружен»), всегда используйте ExternalEvent. Это гарантирует, что основной поток 1С обработает ваше сообщение корректно в рамках своего жизненного цикла.
Если же вы строите сложную интеграцию, где DLL должна выступать в роли «хозяина», управляющего логикой 1С, используйте AppDispatch. В этом случае полезно реализовать в 1С специальный «шлюз» — экспортную функцию в глобальном модуле, которая будет принимать команды от ВК, проверять их на безопасность и выполнять.
В современных версиях 8.3 (особенно при работе в Linux или через веб-клиент) рекомендуется переходить на Native API. В нем вместо IDispatch используется интерфейс IAddInDefBase, но принцип генерации внешних событий остается таким же простым и эффективным.