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