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