Работа с торговым оборудованием в среде «обычных форм» 1С часто преподносит сюрпризы, особенно когда возникает необходимость вызвать модальное окно (например, для подтверждения действия или ввода штрихкода пользователя) прямо в процессе основной работы. Типичная ситуация: программист открывает форму модально, сканирует штрихкод, но... ничего не происходит. Или, что еще хуже, событие обрабатывается формой-родителем, которая в данный момент должна быть заблокирована. Рассмотрим подробно, почему это происходит и как правильно настроить систему для корректной работы.
Для начала проанализируем техническую сторону вопроса. В 1С внешнее событие (ExternalEvent) рассылается платформой по принципу широковещательного сообщения. Когда драйвер оборудования генерирует событие, платформа ищет все открытые формы, в которых определена процедура-обработчик ВнешнееСобытие. Однако модальный режим в обычных формах накладывает жесткие ограничения на стек вызовов. Если модальное окно было открыто из кода, который сам является частью цепочки обработки другого события, или если фокус управления распределен некорректно, событие может «застрять» в очереди сообщений основной формы.
Самый простой и элегантный способ — это проверка активности текущего окна. Если в основной форме прописан обработчик внешнего события, он может продолжать срабатывать даже при наличии модального окна «поверх» него. Чтобы основная форма не перехватывала данные, предназначенные для модального окна, добавим проверку ВводДоступен(). Рассмотрим пример реализации в модуле основной формы:
Процедура ВнешнееСобытие(Источник, Событие, Данные)
// Если открыто модальное окно, ВводДоступен() вернет Ложь
Если Не ВводДоступен() Тогда
Возврат;
КонецЕсли;
// Логика обработки для основной формы
ОбработатьШтрихкод(Данные);
КонецПроцедуры
Этот метод хорош тем, что он минимально инвазивен. Мы просто запрещаем основной форме реагировать на сигналы извне, пока она находится в заблокированном состоянии.
Если метод ВводДоступен() по каким-то причинам не отрабатывает (например, в сложных цепочках вызовов), мы можем реализовать программную блокировку через глобальную переменную или экспортную переменную модуля приложения. Разберем ситуацию, когда мы создаем переменную-флаг НеОбрабатыватьВнешнееСобытие.
В модуле основной формы перед вызовом модального окна мы устанавливаем флаг, а после — снимаем его:
Перем НеОбрабатыватьВнешнееСобытие; // Определяем в начале модуля
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
НеОбрабатыватьВнешнееСобытие = Истина;
Результат = ОткрытьФормуМодально("ОбщаяФорма.ВводШтрихкодаПользователя");
НеОбрабатыватьВнешнееСобытие = Ложь;
КонецПроцедуры
Процедура ВнешнееСобытие(Источник, Событие, Данные)
Если НеОбрабатыватьВнешнееСобытие = Истина Тогда
Возврат;
КонецЕсли;
// Обычная обработка
КонецПроцедуры
Таким образом, мы гарантируем, что пока выполняется код ожидания закрытия модальной формы, основная форма будет игнорировать любые входящие события от сканера.
В типовых решениях (таких как УТ 10.3 или УПП) работа с оборудованием ведется через специализированные обработки (например, ТОСервер или ПолучитьСерверТО()). Чтобы модальная форма «увидела» сканер, она должна быть зарегистрирована в системе как активный «клиент» оборудования. Проанализируем, как это сделать правильно:
Пример кода в модальной форме:
Процедура ПриОткрытии()
// Регистрируем текущую форму как получателя событий ТО
Если ПолучитьСерверТО() <> Неопределено Тогда
ПолучитьСерверТО().ПодключитьКлиента(ЭтаФорма);
КонецЕсли;
КонецПроцедуры
Процедура ПриЗакрытии()
// Освобождаем ресурсы
Если ПолучитьСерверТО() <> Неопределено Тогда
ПолучитьСерверТО().ОтключитьКлиента(ЭтаФорма);
КонецЕсли;
КонецПроцедуры
Процедура ВнешнееСобытие(Источник, Событие, Данные)
// Здесь обрабатываем штрихкод в модальном окне
Штрихкод = СокрЛП(Данные);
ЭтаФорма.Закрыть(Штрихкод);
КонецПроцедуры
Выясним еще одну причину сбоев: попытка открыть модальное окно непосредственно внутри процедуры ВнешнееСобытие основной формы. В этом случае стек вызовов остается «привязанным» к первому событию, и драйвер может не передать второе событие, пока не завершится первое. Чтобы разорвать эту связь, используем ПодключитьОбработчикОжидания.
Рассмотрим алгоритм:
основная форма ловит ШК -> понимает, что нужно открыть окно -> вместо прямого вызова ОткрытьФормуМодально подключает обработчик на 0.1 секунды -> текущая процедура завершается -> платформа открывает окно в «чистом» контексте.
Процедура ВнешнееСобытие(Источник, Событие, Данные)
// Вместо ОткрытьФормуМодально(...)
ПодключитьОбработчикОжидания("ОткрытьОкноВводаДанных", 0.1, Истина);
КонецПроцедуры
Процедура ОткрытьОкноВводаДанных() Экспорт
ОткрытьФормуМодально("ФормаВвода");
КонецПроцедуры
Если модальная форма упорно отказывается принимать внешнее событие напрямую (что иногда случается на старых релизах платформы или при специфических настройках RDP), мы можем использовать обходной путь через метод Оповестить().
Логика такова: основная форма всегда слушает сканер. Если она видит, что открыто модальное окно (определяем по флагу), она принимает данные и «перебрасывает» их в это окно через системное оповещение. Рассмотрим реализацию:
В основной форме:
Процедура ВнешнееСобытие(Источник, Событие, Данные)
Если Не ВводДоступен() Тогда
// Оповещаем все открытые окна (включая модальное)
Оповестить("СканированиеШтрихкода", Данные);
Возврат;
КонецЕсли;
КонецПроцедуры
В модальной форме:
Процедура ОбработкаОповещения(ИмяСобытия, Параметр, Источник)
Если ИмяСобытия = "СканированиеШтрихкода" Тогда
// Обрабатываем данные, полученные от основной формы
РезультатШК = Параметр;
ЭтаФорма.Закрыть(РезультатШК);
КонецЕсли;
КонецПроцедуры
Этот метод является наиболее отказоустойчивым, так как механизм оповещений в 1С работает независимо от модальности окон и фокуса ввода.
Проанализируем ситуацию, когда база работает на сервере. Часто сканеры настраиваются в режиме эмуляции клавиатуры. В модальном режиме фокус может «улетать» с активного поля ввода. Если вы используете внешнее событие через драйвер (COM-объект), убедитесь, что библиотека драйвера проброшена в сессию пользователя корректно. При использовании модальных окон в RDP рекомендуется всегда проверять наличие установленного фокуса в какой-либо элемент формы, иначе внешнее событие может быть проигнорировано платформой как «событие в никуда».
Подводя итог, можно сказать, что лучшей практикой является сочетание Способа 1 (ВводДоступен) для защиты основной формы и Способа 3 (Регистрация клиента ТО) для обеспечения работы модального окна. Если же архитектура конфигурации слишком сложна, Способ 5 (Оповещение) станет гарантированным решением проблемы.