При разработке мобильных рабочих мест (например, для терминалов сбора данных на базе Android) или создании кастомных десктопных интерфейсов для операторов склада и кассиров разработчики часто сталкиваются с неожиданным поведением платформы «1С:Предприятие 8». Нам кажется логичным, что сначала система должна полностью загрузиться, выполнить глобальные процедуры инициализации в модуле управляемого приложения, а уже затем открыть интерфейсное окно — форму начальной страницы (рабочего стола). Однако на практике мы сталкиваемся с тем, что оборудование (сканеры штрихкодов, ТСД, фискальные регистраторы), подключаемое в форме, отказывается работать при старте.
В этой статье мы подробно разберем архитектурную логику запуска системы «1С:Предприятие», выясним точную последовательность выполнения событий и рассмотрим практические способы обхода этой особенности, чтобы ваше оборудование и интерфейсы инициализировались стабильно и без ошибок.
Рассмотрим стандартную ситуацию. Разработчик создает конфигурацию (или дорабатывает существующую, например, на мобильной платформе) для терминала сбора данных (ТСД). На начальный рабочий стол выводится общая форма (назовем ее Форма1), где пользователь должен сразу же начать сканирование штрихкодов. Логика разработчика выглядит следующим образом:
ПриНачалеРаботыСистемы() прописывается код инициализации торгового оборудования (БПО) или глобальных параметров сеанса.Формы1 в обработчике события ПриОткрытии() вызывается функция подключения конкретного сканера к этой форме.Однако при запуске приложения происходит сбой: оборудование не подключается, поскольку службы интеграции еще не запущены. Отладчик показывает удивительную вещь: управление сначала передается в процедуру ПриОткрытии() формы начальной страницы, и только после ее завершения срабатывает глобальный обработчик ПриНачалеРаботыСистемы() модуля управляемого приложения.
Давайте разберемся, почему так происходит, и проанализируем внутренний порядок загрузки платформы.
Описанное поведение не является ошибкой или багом мобильной платформы. Это строго задокументированный базовый алгоритм инициализации интерфейса в «1С:Предприятии 8» (как в десктопной версии, так и в мобильном клиенте/платформе). Посмотрим на пошаговую схему запуска клиентского приложения:
УстановкаПараметровСеанса. Выполняется в модуле сеанса на стороне сервера. Это самый первый этап, на котором подготавливаются ключевые параметры сеанса текущего пользователя.ПередНачаломРаботыСистемы. Данное событие выполняется на клиенте в модуле управляемого приложения. На этом шаге интерфейс пользователя еще физически не создан. Здесь категорически нельзя открывать интерактивные формы (за исключением простейших диалогов предупреждения). В этой процедуре можно проверить условия запуска и, при необходимости, отказаться от старта системы, передав в параметр Отказ значение Истина.ПриСозданииНаСервере() формы рабочего стола;ПриОткрытии() формы рабочего стола.ПриНачалеРаботыСистемы. Это событие модуля управляемого приложения вызывается строго после того, как главное окно и формы начальной страницы уже созданы, открыты и выведены на экран. Отказаться от запуска системы на данном этапе уже невозможно.ПослеНачалаРаботыСистемы().Таким образом, формы, расположенные на рабочем столе (начальной странице), всегда инициализируются и открываются раньше, чем начинает выполняться основной инициализационный код в процедуре ПриНачалеРаботыСистемы().
Если ваше оборудование инициализируется стандартными механизмами Библиотеки подключаемого оборудования (БПО) в процедуре ПриНачалеРаботыСистемы(), то при открытии формы рабочего стола службы БПО еще не запущены, а глобальные клиентские переменные не заполнены. Попытка вызвать подключение устройства в этот момент гарантированно вызовет ошибку.
Для обхода этой архитектурной особенности существует несколько проверенных временем решений. Рассмотрим их подробно и выберем наиболее эффективные практики.
Это самый популярный, надежный и простой в реализации способ. Смысл заключается в том, чтобы отложить подключение оборудования в форме до тех пор, пока платформа не завершит все стартовые процедуры (включая ПриНачалеРаботыСистемы) и не вернет управление в интерфейсный поток.
Вместо того чтобы выполнять подключение непосредственно в ПриОткрытии(), мы запускаем быстрый однократный таймер. Разберем этот механизм на примере кода:
&НаКлиенте
Процедура ПриОткрытии(Отказ)
// Запускаем однократный обработчик ожидания с минимальной задержкой (0.1 секунды)
ПодключитьОбработчикОжидания("ПодключитьОборудованиеПослеСтарта", 0.1, Истина);
КонецПроцедуры
&НаКлиенте
Процедура ПодключитьОборудованиеПослеСтарта() Экспорт
// Этот код гарантированно выполнится ПОСЛЕ завершения работы процедуры ПриНачалеРаботыСистемы()
Попытка
ИнициализироватьОборудованиеФормы();
Исключение
ЗаписатьЛогОшибки(ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
Как это работает: Платформа обрабатывает событие ПриОткрытии(), регистрирует обработчик ожидания и сразу же завершает процедуру формы. После этого платформа переходит к выполнению глобального события ПриНачалеРаботыСистемы(), где успешно инициализирует БПО и всё ваше оборудование. Как только управление возвращается в интерфейсный поток (через 0.1 сек), срабатывает наша процедура ПодключитьОборудованиеПослеСтарта(), которая видит уже полностью инициализированную систему и успешно подключает сканер ТСД.
Если для запуска служб оборудования или загрузки внешних компонент (драйверов) не требуется выводить пользователю диалоговые окна и формы (то есть весь процесс происходит в неинтерактивном фоновом режиме), мы можем перенести базовую инициализацию в событие ПередНачаломРаботыСистемы() модуля управляемого приложения.
Поскольку событие ПередНачаломРаботыСистемы() выполняется гарантированно до создания каких-либо элементов интерфейса и форм рабочего стола, к моменту открытия нашей формы начальной страницы все библиотеки и переменные уже будут полностью готовы к работе.
// В модуле управляемого приложения
Процедура ПередНачаломРаботыСистемы(Отказ)
// Инициализируем только неинтерактивные структуры данных и загружаем компоненты
ИнициализироватьДрайверыОборудования();
КонецПроцедуры
Однако будьте осторожны: если в процессе инициализации оборудования в ПередНачаломРаботыСистемы() произойдет сбой, требующий вывода интерактивного окна пользователю (например, предложение настроить порт или выбрать устройство), платформа не сможет корректно отобразить это окно, что приведет к ошибке выполнения.
Часто программисты стремятся подключить все возможные службы «на старте», даже если они не понадобятся пользователю сию секунду. Чтобы избежать конфликтов при запуске, мы можем использовать принцип ленивой (lazy) загрузки.
Не подключайте сканер штрихкодов или терминал непосредственно при открытии рабочего стола. Вместо этого выполняйте подключение:
Посмотрим на пример реализации ленивой инициализации при нажатии кнопки начала работы:
&НаКлиенте
Процедура НачатьРаботуВыполнить(Кнопка)
// Оборудование подключается только тогда, когда пользователь явно изъявил желание работать
Если Не ОборудованиеПодключено Тогда
ПодключитьРабочееОборудование();
КонецЕсли;
ПерейтиКРежимуСканирования();
КонецПроцедуры
Этот подход не только решает проблему с порядком загрузки, но и экономит ресурсы мобильного устройства (батарею ТСД, оперативную память), так как внешние компоненты не будут висеть в памяти, если пользователь просто зашел посмотреть отчет или справочник на рабочем столе.
Если ваша конфигурация разработана на основе Библиотеки стандартных подсистем (БСП), настоятельно рекомендуется использовать встроенные механизмы интеграции. В БСП предусмотрен общий переопределяемый модуль, в котором разработчики могут переопределить процедуру ПослеНачалаРаботыСистемы().
Этот обработчик спроектирован специально для решения подобных проблем. К моменту его вызова платформа уже открыла рабочий стол, отработали все процедуры модуля приложения, а БСП завершила свою внутреннюю настройку параметров. Именно здесь безопаснее всего открывать модальные (или блокирующие весь интерфейс) окна помощников настройки оборудования или выполнять финальное подключение периферии.
Проанализировав поведение платформы 1С, мы выяснили важную деталь: для форм рабочего стола (начальной страницы) событие ПриОткрытии() всегда срабатывает раньше, чем глобальное событие ПриНачалеРаботыСистемы().
Для решения этой проблемы используйте простой и надежный механизм отложенного вызова через ПодключитьОбработчикОжидания() с минимальным интервалом времени. Это позволит вам сохранить логику инициализации оборудования в модуле управляемого приложения и гарантирует корректную работу ТСД и сканеров как на мобильных платформах, так и на настольных ПК.