Как правильно инициализировать и хранить внешнюю компоненту Native API в 1С

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

При разработке механизмов взаимодействия с торговым оборудованием, таким как электронные весы, сканеры штрихкодов или фискальные регистраторы, программисты часто сталкиваются с необходимостью оптимизировать процесс подключения. Основная задача заключается в том, чтобы инициализировать драйвер (внешнюю компоненту) один раз при запуске системы и обращаться к уже созданному объекту в течение всего сеанса работы пользователя. Однако попытки сохранить этот объект в привычные ПараметрыСеанса или ВременноеХранилище приводят к ошибкам инициализации. Рассмотрим подробно, почему это происходит и как правильно организовать работу с Native API.

Почему возникает ошибка при использовании параметров сеанса?

Разберем техническую причину ограничения. Внешние компоненты, созданные по технологии Native API, представляют собой объекты, существующие в адресном пространстве конкретного процесса — в данном случае, в памяти тонкого или толстого клиента 1С. Когда мы выполняем команду Новый("AddIn.ИмяКомпоненты"), система создает в памяти экземпляр объекта и возвращает ссылку на него.

Проблема заключается в сериализации. Чтобы поместить любой объект в ПараметрыСеанса или ВременноеХранилище, платформа 1С должна «сериализовать» его, то есть превратить в последовательность байтов, которую можно передать по сети на сервер и сохранить в базе данных или памяти сервера. Объекты внешних компонент не поддерживают сериализацию. Они являются «живыми» указателями на методы драйвера. Как только мы пытаемся передать такой объект с клиента на сервер, платформа выдает ошибку, так как не знает, как упаковать состояние драйвера оборудования для передачи.

Важный нюанс: параметры сеанса всегда живут на стороне сервера, а торговое оборудование (весы) подключено к клиентскому рабочему месту. Пытаться «засунуть» драйвер весов на сервер — технически неверная стратегия.

Использование глобального контекста ПараметрыПриложения

Для решения задачи нам необходимо хранить объект там, где он был создан — на стороне клиента, и при этом обеспечить к нему доступ из различных форм и модулей. Наиболее подходящим местом для этого в управляемом приложении является свойство глобального контекста ПараметрыПриложения. Это коллекция типа Соответствие, которая доступна в течение всей жизни клиентского сеанса.

Проанализируем правильный алгоритм инициализации компоненты. Рассмотрим пример функции, которая проверяет наличие подключения и, если его нет, инициализирует весы:


Функция ПодключитьВесы() Экспорт
    // Проверяем, инициализирована ли уже глобальная переменная параметров
    Если ПараметрыПриложения = Неопределено Тогда
        ПараметрыПриложения = Новый Соответствие;
    КонецЕсли;

    // Проверяем, нет ли уже подключенного объекта
    Весы = ПараметрыПриложения.Получить("ПодключенныеВесы");
    Если Весы <> Неопределено Тогда
        Возврат Весы;
    КонецЕсли;

    Попытка
        // ПутьКомпонента - путь к файлу или имя макета
        ПодключитьВнешнююКомпоненту(ПутьКомпонента, "ScalesDriver", ТипВнешнейКомпоненты.Native);
        НовыеВесы = Новый ("AddIn.ScalesDriver");
        
        // Сохраняем объект в глобальном соответствии на клиенте
        ПараметрыПриложения.Вставить("ПодключенныеВесы", НовыеВесы);
        
        Возврат НовыеВесы;
    Исключение
        Сообщить("Ошибка загрузки компоненты весов: " + ОписаниеОшибки());
        Возврат Неопределено;
    КонецПопытки;
КонецФункции

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

Организация доступа через модуль с повторным использованием значений

Чтобы код был чище и соответствовал стандартам разработки, получение объекта весов лучше вынести в отдельный общий модуль. Рассмотрим вариант использования модуля с признаком "Повторное использование значений на время сеанса". Хотя сам объект компоненты в кэш модуля повторного использования помещать нельзя (по тем же причинам отсутствия сериализации при переходе контекстов), мы можем использовать его для логической проверки состояния.

Однако более надежным методом остается создание клиентского общего модуля (например, РаботаСОборудованиемКлиент), в котором будет реализована функция получения драйвера:


Функция ПолучитьОбъектВесов() Экспорт
    // Пытаемся достать из параметров приложения
    Если ПараметрыПриложения <> Неопределено Тогда
        Результат = ПараметрыПриложения.Получить("ПодключенныеВесы");
        Если Результат <> Неопределено Тогда
            Возврат Результат;
        КонецЕсли;
    КонецЕсли;
    
    // Если объекта нет - инициализируем его
    Возврат ПодключитьВесы(); 
КонецФункции

Реализация непрерывного взвешивания через обработчик ожидания

Часто задача требует не просто разового получения веса, а постоянного мониторинга состояния весов (например, до момента стабилизации веса). Поскольку мы сохранили объект компоненты в глобальном кэше, мы можем легко запустить цикл опроса, не опасаясь утечек памяти или постоянных переподключений к порту.

Разберем, как реализовать опрос в форме документа:

  1. В процедуре ПриОткрытии или при нажатии кнопки «Взвесить» подключаем обработчик ожидания.
  2. В самой процедуре обработчика обращаемся к нашей глобальной переменной.
  3. При закрытии формы или достижении результата отключаем обработчик.

Пример кода в модуле формы:


&НаКлиенте
Процедура НачатьПолучениеВеса()
    ПодключитьОбработчикОжидания("ОбработчикОпросаВесов", 0.5); // Опрос каждые полсекунды
КонецПроцедуры

&НаКлиенте
Процедура ОбработчикОпросаВесов()
    Драйвер = РаботаСОборудованиемКлиент.ПолучитьОбъектВесов();
    Если Драйвер = Неопределено Тогда
        ОтключитьОбработчикОжидания("ОбработчикОпросаВесов");
        Возврат;
    КонецЕсли;

    ТекущийВес = Драйвер.ПолучитьВес(); // Метод конкретной компоненты
    Статус = Драйвер.СтатусСтабильности;
    
    Объект.Вес = ТекущийВес;
    
    Если Статус = 1 Тогда // Например, 1 - вес стабилизирован
        ОтключитьОбработчикОжидания("ОбработчикОпросаВесов");
        ПоказатьОповещениеПользователя("Взвешивание завершено");
    КонецЕсли;
КонецПроцедуры

Меры предосторожности и жизненный цикл объекта

При работе с внешними компонентами через ПараметрыПриложения важно помнить о следующих моментах:

1. Конфликты имен. Поскольку ПараметрыПриложения — это общая коллекция, используйте префиксы для ключей. Вместо "ПодключенныеВесы" лучше использовать что-то вроде "МояПодсистема_ДрайверВесов", чтобы избежать пересечения с типовыми механизмами (например, БСП также может использовать эту коллекцию).

2. Освобождение ресурсов. Если компонента монопольно захватывает COM-порт, она должна корректно его освобождать. Рекомендуется в модуле управляемого приложения в процедуре ПриЗавершенииРаботыСистемы явно прописать очистку:


&НаКлиенте
Процедура ПриЗавершенииРаботыСистемы()
    Если ПараметрыПриложения <> Неопределено Тогда
        Весы = ПараметрыПриложения.Получить("ПодключенныеВесы");
        Если Весы <> Неопределено Тогда
            Весы.Отключить(); // Если у компоненты есть метод отключения
            ПараметрыПриложения.Вставить("ПодключенныеВесы", Неопределено);
        КонецЕсли;
    КонецЕсли;
КонецПроцедуры

3. Асинхронность. Начиная с версии платформы 8.3.18+, рекомендуется переходить на асинхронные методы работы с компонентами (НачатьПодключениеВнешнейКомпоненты). Это обеспечит работоспособность вашего кода не только в тонком клиенте, но и в веб-клиенте, где синхронные вызовы блокируются браузером. Логика хранения в ПараметрыПриложения при этом остается прежней, меняется только способ инициализации.

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

← На главную