Как бороться с «Серверные вызовы при завершении работы запрещены»? |
Я |
01.09.17 — 15:41
При закрытии обработки сохраняю настройки в хранилище. Всё ок, но если попытаться закрыть 1С при открытой обработке, появляется ошибка
«Серверные вызовы при завершении работы запрещены»
Можно как-нибудь обойти? Не сохранять же настройки при каждом изменении
1 — 01.09.17 — 15:41
в ОФ все реализовано
2 — 01.09.17 — 15:43
Если в ПриЗакрытии выполняется сохранение настроек формы, то следует отказаться от обработчика ПриЗакрытии и перенести этот код в обработчики событий, вызываемые при действиях пользователя.
Было:
&НаКлиенте
Процедура ПриЗакрытии()
СохранитьНастройки();
КонецПроцедуры
&НаСервере
Процедура СохранитьНастройки()
// Сохраняем дату последнего отображения.
ОбщегоНазначения.ХранилищеОбщихНастроекСохранить(«Настройка», » ДатаПоследнегоОтображенияПредложения», ТекущаяДатаСеанса());
КонецПроцедуры
Стало:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
…
// Сохраняем дату последнего отображения.
ОбщегоНазначения.ХранилищеОбщихНастроекСохранить(«Настройка», «ДатаПоследнегоОтображенияПредложения», ТекущаяДатаСеанса());
…
КонецПроцедуры
Если событие, которое возникает при действиях пользователя, вызывается на стороне клиента, рекомендуется выполнять сохранение настроек в безконтекстном серверном вызове, чтобы избежать перерисовки формы:
&НаКлиенте
Процедура НапомнитьПозже(Команда)
СохранитьНастройки();
КонецПроцедуры
&НаСервереБезКонтекста
Процедура СохранитьНастройки()
// Сохраняем дату последнего отображения.
ОбщегоНазначения.ХранилищеОбщихНастроекСохранить(«Настройка», «ДатаПоследнегоОтображенияПредложения», ТекущаяДатаСеанса());
КонецПроцедуры
3 — 01.09.17 — 15:43
4 — 01.09.17 — 15:44
Теперь принято сохранять настройки не при закрытии, а при изменении настроек
5 — 01.09.17 — 15:50
http://i0.kym-cdn.com/entries/icons/mobile/000/005/848/Aliens.jpg
Т.е. если у меня есть большая ТЧ с настройками, то мне её при каждом чихе (по окончанию редактирования строки) сохранять надо?? Ужс.
За инфу спасибо.
6 — 01.09.17 — 15:56
не сохраняй всю тз, разбей ее на блоки.
настройки лучше поштучно хранить
7 — 01.09.17 — 16:00
(6) В тз у меня, например, суммы тарифов в зависимости от количества документов. Т.е. будут строки вида:
до 1 дока — 100 р
до 2 доков — 150 р
до 5 доков — 300 р
и т.д. Тут нечего разбивать на блоки.
Ну или например у меня на форме поле Контрагент, которое нужно сохранить для всех пользователей. Это получается, что при каждом изменении нужно на сервер бегать. Не понимаю.
8 — 01.09.17 — 16:02
(7) Пишите письма (в спортлото)
9 — 01.09.17 — 16:02
(7) Добро пожаловать в мир управляемых форм. Пилотам Апача еще тяжелей — у них глаза привыкают смотреть в разные стороны — один смотрит куда летит, второй — куда стреляет.
10 — 01.09.17 — 16:13
Я сделаль!
&НаКлиенте Процедура ПередЗакрытием(Отказ, ЗавершениеРаботы, ТекстПредупреждения, СтандартнаяОбработка) Попытка СохранитьВсеНастройки(); Исключение Если ЗавершениеРаботы Тогда Отказ = Истина; КонецЕсли; КонецПопытки; КонецПроцедуры
11 — 01.09.17 — 16:18
но ничего не сохранится ведь
12 — 01.09.17 — 16:23
(11) Прекратите. Это совершенно не важно. Пользователь не заметит, свинья не съест )
13 — 01.09.17 — 16:26
Типовая КА 2.2 время от времени отображает такое сообщение, хотя ни строчки кода не изменено
14 — 01.09.17 — 16:30
(11) При завершении работы вылезет окно «Работа в данном окне не завершена». Далее пользователь жмёт «Продолжить работу», закрывает форму обработку, закрывает 1С.
Если нажмёт «Завершить работу», то да, ничего не сохранится, но его предупреждали)
Писать в каждый элемент процедуру «ПриИзменении», по-моему, та ещё дичь.
15 — 01.09.17 — 16:37
Добавил немного UX)
&НаКлиенте Процедура ПередЗакрытием(Отказ, ЗавершениеРаботы, ТекстПредупреждения, СтандартнаяОбработка) Попытка СохранитьВсеНастройки(); Исключение Если ЗавершениеРаботы Тогда Сообщение = Новый СообщениеПользователю; Сообщение.Текст = "Чтобы сохранились настройки, данную форму нужно закрыть руками. Спасибо за понимание :)"; Сообщение.ИдентификаторНазначения = УникальныйИдентификатор; Сообщение.Сообщить(); Отказ = Истина; КонецЕсли; КонецПопытки; КонецПроцедуры
16 — 02.09.17 — 01:37
А если сделать отказ от закрытия, сохранить настройки — поднять флаг возможности сохранения и снова выполнить «завершить работу» ?
17 — 02.09.17 — 06:47
(16) Вот именно так я и делал! По другому не выходит
18 — 02.09.17 — 06:48
(17) + обработку ожидания секундную разовую, в которой все закрывалось с флагом без сохранения
19 — 02.09.17 — 09:33
(15) Силён.
20 — 02.09.17 — 11:38
(7) >В тз у меня, например, суммы тарифов в зависимости от количества документов
К слову, это уже не настройки пользователя, эти данные должны жить в базе.
А вообще, это конечно проблема. Точнее недоработка платформы. Со времени появления веб клиента прогресс не стоял на месте. В браузерах появились веб воркеры и прочие ништяки. Можно уже реализовать и сохранение настроек в фоне после закрытия формы, и асинхронную загрузку гигабайтных файлов из соседней темы.
DTX 4th
21 — 05.09.17 — 10:32
(16) Так программно отказ от закрытия не сделать. Но вроде неплохой вариант.
(20) >К слову, это уже не настройки пользователя, эти данные должны жить в базе.
В типовой? В хранилище общих настроек?
Прост один и тот же файл может быть использован в разных базах.
Нужно выполнять авто обмен с РИБ перед завершение работы системы, как это можно было делать и ранее.
Сейчас, как видно из ошибки выполнить действия, которые требуют серверную часть модуля нельзя. А как же выполнить обмен с РИБ перед закрытием платформы если без серверных методов не обойтись?!
Ничего лучшего кроме как
Процедура ПередЗавершениемРаботыСистемы(Отказ, ТекстПредупреждения)
Отказ = Истина;
ТекстПредупреждения = «Нажмите Продолжить работу, что бы выполнить АвтоОбменРИБ»;
ПодключитьОбработчикОжидания(«ВыполнитьОбмен»,1,Истина);
КонецПроцедуры
Процедура ВыполнитьОбмен()Экспорт
Обмен.ВыполнитьОбменСоВсемаРИБ();
ОтключитьОбработчикОжидания(«ВыполнитьОбмен»);
КонецПроцедуры
не придумал.
Тут все просто, перед завершением работы платформы, пользователю как бы намекаем, что «нажми продолжить». Если нажата кнопка «продолжить» то программа не завершиться, а передаст свое выполнение общему модулю с серверным контекстом где уже и осуществляется обмен со всеми РИБ.
При закрытии обработки сохраняю настройки в хранилище. Всё ок, но если попытаться закрыть 1С при открытой обработке, появляется ошибка «Серверные вызовы при завершении работы запрещены» Можно как-нибудь обойти? Не сохранять же настройки при каждом изменении
Если в ПриЗакрытии выполняется сохранение настроек формы, то следует отказаться от обработчика ПриЗакрытии и перенести этот код в обработчики событий, вызываемые при действиях пользователя. Было: &НаКлиенте Стало: &НаСервере … КонецПроцедуры Если событие, которое возникает при действиях пользователя, вызывается на стороне клиента, рекомендуется выполнять сохранение настроек в безконтекстном серверном вызове, чтобы избежать перерисовки формы: &НаКлиенте
Теперь принято сохранять настройки не при закрытии, а при изменении настроек
Т.е. если у меня есть большая ТЧ с настройками, то мне её при каждом чихе (по окончанию редактирования строки) сохранять надо?? Ужс. За инфу спасибо.
не сохраняй всю тз, разбей ее на блоки. настройки лучше поштучно хранить
В тз у меня, например, суммы тарифов в зависимости от количества документов. Т.е. будут строки вида: до 1 дока — 100 р до 2 доков — 150 р до 5 доков — 300 р и т.д. Тут нечего разбивать на блоки. Ну или например у меня на форме поле Контрагент, которое нужно сохранить для всех пользователей. Это получается, что при каждом изменении нужно на сервер бегать. Не понимаю.
Пишите письма (в спортлото)
Добро пожаловать в мир управляемых форм. Пилотам Апача еще тяжелей — у них глаза привыкают смотреть в разные стороны — один смотрит куда летит, второй — куда стреляет.
но ничего не сохранится ведь
Прекратите. Это совершенно не важно. Пользователь не заметит, свинья не съест )
Типовая КА 2.2 время от времени отображает такое сообщение, хотя ни строчки кода не изменено
При завершении работы вылезет окно «Работа в данном окне не завершена». Далее пользователь жмёт «Продолжить работу», закрывает форму обработку, закрывает 1С. Если нажмёт «Завершить работу», то да, ничего не сохранится, но его предупреждали) Писать в каждый элемент процедуру «ПриИзменении», по-моему, та ещё дичь.
А если сделать отказ от закрытия, сохранить настройки — поднять флаг возможности сохранения и снова выполнить «завершить работу» ?
Вот именно так я и делал! По другому не выходит
+ обработку ожидания секундную разовую, в которой все закрывалось с флагом без сохранения
>В тз у меня, например, суммы тарифов в зависимости от количества документов К слову, это уже не настройки пользователя, эти данные должны жить в базе. А вообще, это конечно проблема. Точнее недоработка платформы. Со времени появления веб клиента прогресс не стоял на месте. В браузерах появились веб воркеры и прочие ништяки. Можно уже реализовать и сохранение настроек в фоне после закрытия формы, и асинхронную загрузку гигабайтных файлов из соседней темы.
Так программно отказ от закрытия не сделать. Но вроде неплохой вариант. >К слову, это уже не настройки пользователя, эти данные должны жить в базе. В типовой? В хранилище общих настроек? Прост один и тот же файл может быть использован в разных базах.
Тэги:
Комментарии доступны только авторизированным пользователям
Все ниже описанное касается управляемого приложения.
Начиная с версии 8.3.8 фирма 1С изменила подход к завершению работы и закрытию приложения. Новомодные веяния – ли, ошибки при работе с WEB интерфейсом – ли, но теперь со всех обработчиков событий, используемых нами при закрытии – «ПередЗавершениемРаботыСистемы» и «ПриЗавершенииРаботыСистемы», сервер не доступен. Рассмотрим, как теперь работают данные обработчики.
Contents
- 1 Обработчик: ПередЗавершениемРаботыСистемы(Отказ, ТекстПредупреждения)
- 2 Обработчик: ПриЗавершенииРаботыСистемы()
- 3 Резюме
Обработчик: ПередЗавершениемРаботыСистемы(Отказ, ТекстПредупреждения)
Как мы помним возникает перед завершением программы, до закрытия главного окна. Если «Отказ» равен «Ложь», то программа просто закрывается, не выдавая никаких вопросов. В нем можно сделать всяческие проверки, все функции должны быть только клиентские (В процессе завершения работы приложения запрещены серверные вызовы и открытие окон.) и может быть установлен отказ от выхода из программы. И вот тут появляется первый подвох. Установление «Отказ» в Истина не отменяет выход из системы, а только указывает, что нужно выдать платформенный диалог, в котором будет предложено выйти или остаться с текстом из параметра «ТекстПредупреждения»:
И если пользователь нажмет «Завершить работу», все ваши проверки идут лесом. Платформа просто закрывает приложение не зависимо ни от чего. Отловить этот момент в коде нельзя, это все происходит на уровне платформы.
Если же пользователь нажмет на «Продолжить работу», то возможны два варианта событий:
- Если вы внутри вызова функций из данной процедуры подключали обработчик ожидания
ПодключитьОбработчикОжидания("ИмяФункцииОбработчикаОжидания", 0.1, Истина);
тогда программа перейдет на этот обработчик, и выполнятся все действия которые вы в нем задали, даже вызов серверных функций.
- Если же вы этого не делали, программа просто продолжит работу.
Ответ на первый глобальный русский вопрос «кто виноват?» понятен. Я постараюсь ответить на второй «что делать?». Если нам надо что то зафиксировать в базе, например информацию о работе пользователей в системе. Кто когда заходил, кто когда вышел.
Решение однако лежит вообще в другой плоскости, а именно, в регламентном задании которое периодически будет опрашивать систему на активных пользователей, и фиксировать что такой то пользователь уже отсутствует в списке активных.
Для опроса системы у нас есть функция ПолучитьСеансыИнформационнойБазы(), которая возвращает массив сеансов. А для фиксации, что пользователя в сеансах уже нет нужно создавать новый регистр. Я предлагаю периодический (в пределах секунды) РС с двумя измерениями «Пользователь» и «НомерСеанса», в ресурсах же должно быть как минимум поле «ЗавершениеСеанса» по которому мы будем понимать что пользователь нами не обработан или обработан но активен.
Процедура ОбработатьСеансыАктивныхПользователей() Экспорт ТекущаяДатаСеанса = ТекущаяДатаСеанса(); ТабАктивныхПользователей = Новый ТаблицаЗначений; ТабАктивныхПользователей.Колонки.Добавить("Пользователь", Новый ОписаниеТипов("СправочникСсылка.Пользователи")); ТабАктивныхПользователей.Колонки.Добавить("НомерСеанса", Новый ОписаниеТипов("Число")); СеансыИнформационнойБазы = ПолучитьСеансыИнформационнойБазы(); Для Каждого СеансИБ Из СеансыИнформационнойБазы Цикл //Фоновые задания или неавторизованных пользователей пропускаем Если СеансИБ.ПолучитьФоновоеЗадание() <> Неопределено ИЛИ (ТипЗнч(СеансИБ.Пользователь) = Тип("ПользовательИнформационнойБазы") И НЕ ЗначениеЗаполнено(СеансИБ.Пользователь.Имя)) Тогда Продолжить; КонецЕсли; АктивныйПользователь = ДобавитьЗаписьАвторизацииПользователей(СеансИБ, ТекущаяДатаСеанса); Если ЗначениеЗаполнено(АктивныйПользователь) Тогда НоваяСтрока = ТабАктивныхПользователей.Добавить(); НоваяСтрока.Пользователь = АктивныйПользователь; НоваяСтрока.НомерСеанса = СеансИБ.НомерСеанса; КонецЕсли; КонецЦикла; Запрос = Новый Запрос; Запрос.Текст = "ВЫБРАТЬ | ккПериодическиеСведенияОПользователяхСрезПоследних.Период КАК Период, | ккПериодическиеСведенияОПользователяхСрезПоследних.Пользователь КАК Пользователь, | ккПериодическиеСведенияОПользователяхСрезПоследних.НомерСеанса КАК НомерСеанса |ИЗ | РегистрСведений.ккПериодическиеСведенияОПользователях.СрезПоследних( | &ДатаРегистрации, | НЕ (Пользователь, НомерСеанса) В (&ТабРаботающих) | И ЗавершенияСеанса = ДатаВремя(1,1,1)) КАК ккПериодическиеСведенияОПользователяхСрезПоследних"; Запрос.УстановитьПараметр("ДатаРегистрации", ТекущаяДатаСеанса); Запрос.УстановитьПараметр("ТабРаботающих", ТабАктивныхПользователей); Результат = Запрос.Выполнить(); Выборка = Результат.Выбрать(); Пока Выборка.Следующий() Цикл НаборЗаписей = РегистрыСведений.ккПериодическиеСведенияОПользователях.СоздатьНаборЗаписей(); НаборЗаписей.Отбор.Период.Установить(Выборка.Период); НаборЗаписей.Отбор.Пользователь.Установить(Выборка.Пользователь); НаборЗаписей.Отбор.НомерСеанса.Установить(Выборка.НомерСеанса); НаборЗаписей.Прочитать(); Для каждого СведенияОПользователе Из НаборЗаписей Цикл СведенияОПользователе.ЗавершенияСеанса = ТекущаяДатаСеанса; СведенияОПользователе.ВремяВСеансе = СведенияОПользователе.ЗавершенияСеанса - СведенияОПользователе.Период; КонецЦикла; Попытка Если НаборЗаписей.Количество() Тогда НаборЗаписей.Записать(); КонецЕсли; Исключение КонецПопытки; КонецЦикла; КонецПроцедуры
Функция ДобавитьЗаписьАвторизацииПользователей(СеансИБ, ТекущаяДатаСеанса) Экспорт ТекущийПользователь = НайтиСсылкуПоИдентификаторуПользователя(СеансИБ.Пользователь.УникальныйИдентификатор); Если НЕ ЗначениеЗаполнено(ТекущийПользователь) Тогда Возврат Неопределено; КонецЕсли; Запрос = Новый Запрос; Запрос.Текст = "ВЫБРАТЬ ПЕРВЫЕ 1 | 1 КАК Поле |ИЗ | РегистрСведений.ккПериодическиеСведенияОПользователях.СрезПоследних( | &ДатаРегистрации, | Пользователь = &Пользователь | И НомерСеанса = &НомерСеанса | И ЗавершенияСеанса = ДАТАВРЕМЯ(1, 1, 1)) КАК ккПериодическиеСведенияОПользователяхОстатки"; Запрос.УстановитьПараметр("ДатаРегистрации", ТекущаяДатаСеанса); Запрос.УстановитьПараметр("Пользователь", ТекущийПользователь); Запрос.УстановитьПараметр("НомерСеанса", СеансИБ.НомерСеанса); Результат = Запрос.Выполнить(); НаборЗаписей = РегистрыСведений.ккПериодическиеСведенияОПользователях.СоздатьНаборЗаписей(); НаборЗаписей.Отбор.Период.Установить(СеансИБ.НачалоСеанса); НаборЗаписей.Отбор.Пользователь.Установить(ТекущийПользователь); НаборЗаписей.Отбор.НомерСеанса.Установить(СеансИБ.НомерСеанса); НаборЗаписей.Прочитать(); Если НЕ Результат.Пустой() Тогда Если НаборЗаписей.Количество() Тогда СведенияОПользователе = НаборЗаписей[0]; СведенияОПользователе.ВремяВСеансе = ТекущаяДатаСеанса - СведенияОПользователе.Период; КонецЕсли; Иначе СведенияОПользователе = НаборЗаписей.Добавить(); СведенияОПользователе.Период = СеансИБ.НачалоСеанса; СведенияОПользователе.Пользователь = ТекущийПользователь; СведенияОПользователе.НомерСеанса = СеансИБ.НомерСеанса; СведенияОПользователе.Подключение = 1; СведенияОПользователе.ВремяВСеансе = ТекущаяДатаСеанса - СведенияОПользователе.Период; СведенияОПользователе.ИмяПриложения = ПредставлениеПриложения(СеансИБ.ИмяПриложения); СведенияОПользователе.ИмяКомпьютера = СеансИБ.ИмяКомпьютера; КонецЕсли; Попытка НаборЗаписей.Записать(); Исключение КонецПопытки; Возврат ТекущийПользователь; КонецФункции Функция НайтиСсылкуПоИдентификаторуПользователя(Идентификатор) // Нет доступа к разделенному справочнику из неразделенного сеанса. Если ОбщегоНазначения.РазделениеВключено() И Не ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда Возврат Неопределено; КонецЕсли; Запрос = Новый Запрос; ШаблонТекстаЗапроса = "ВЫБРАТЬ | Ссылка КАК Ссылка |ИЗ | %1 |ГДЕ | ИдентификаторПользователяИБ = &Идентификатор"; ТекстЗапросаПоПользователям = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонТекстаЗапроса, Метаданные.Справочники.Пользователи.ПолноеИмя()); ТекстЗапросаПоВнешнимПользователям = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонТекстаЗапроса, Метаданные.Справочники.ВнешниеПользователи.ПолноеИмя()); Запрос.Текст = ТекстЗапросаПоПользователям; Запрос.Параметры.Вставить("Идентификатор", Идентификатор); Результат = Запрос.Выполнить(); Если НЕ Результат.Пустой() Тогда Выборка = Результат.Выбрать(); Выборка.Следующий(); Возврат Выборка.Ссылка; КонецЕсли; Запрос.Текст = ТекстЗапросаПоВнешнимПользователям; Результат = Запрос.Выполнить(); Если НЕ Результат.Пустой() Тогда Выборка = Результат.Выбрать(); Выборка.Следующий(); Возврат Выборка.Ссылка; КонецЕсли; Возврат Справочники.Пользователи.ПустаяСсылка(); КонецФункции
Обработчик: ПриЗавершенииРаботыСистемы()
Привожу полный текст справки:
«Описание:
Возникает перед завершением работы в режиме управляемого приложения после закрытия главного окна. В данной процедуре могут быть выполнены действия, необходимые при выходе из программы.
Примечание:
В данной процедуре не допускаются:
- открытие форм и других окон,
- использование серверных вызовов,
- использование асинхронных вызовов.
Не поддерживаются:
- выдача сообщений,
- установка текста в панели состояния,
- другие действия, требующие наличия главного окна.»
Вот честно, не понимаю вообще какие действия можно осуществить в данной процедуре? Да мы можем написать кучу кода, что-то сделать с файлами, но ничего сообщить пользователю, что то сделать с базой – мы уже не можем.
Резюме
Резюмируя данную статью хочу поблагодарить фирму 1С, что не дает нам соскучиться и всегда на страже нашего свободного времени.
Эта статья для программистов 1С – в ней разберем новое ограничение платформы при разработке кода.
О чем пойдет речь?
При закрытии формы обработки или при закрытии всего приложения достаточно часто используется дополнительный диалог.
В основной массе диалог представляет собой вопрос для подтверждения операции закрытия, но бывают более сложные случаи. Например, при закрытии формы подбора товаров можно запросить у пользователя разрешение на перенос подобранных товаров в документ.
Начиная с версии 8.3.8 разработчики платформы изменили подход, которому должны следовать программисты при решении подобного рода задач.
Коротко о главном…
Сначала коротко обозначим суть изменений. Начиная с версии платформы 8.3.8 необходимо выделять 2 сценария закрытия формы:
- Первый вариант – когда пользователь закрывает форму самостоятельно
- Второй вариант – когда форма закрывается в результате закрытия всего приложения.
Как это повлияло на объем кода?
- Немного увеличился код при закрытии формы, так как необходимо отрабатывать 2 сценария
- Практически до минимума упростился код при закрытии приложения (речь идет о подтверждении выхода из системы).
Кроме этого, изменения платформы серьезно затронули логику программного кода. Теперь в процессе закрытия приложения практически невозможно организовать произвольный сценарий из-за введенных ограничений. В чем суть ограничений?
- В процессе закрытия приложения запрещено открытие любых форм и диалогов
- В процессе закрытия приложения запрещены любые серверные вызовы
- Нельзя программным способом отменить закрытие приложения без отображения вопроса пользователю.
Фактически теперь в процессе закрытия приложения можно указать только текст вопроса, который увидит пользователь.
Скорее всего, у вас уже возник вопрос: к чему же все эти изменения?
Приведем цитату разработчиков платформы из файла V8Update.htm, содержащего перечень изменений релизов платформы:
Реализован единый подход к закрытию форм и основного окна приложения, который одинаково работает во всех клиентских приложениях, обеспечивая приемлемые возможности по взаимодействию с пользователями. Реализованное поведение стало соответствовать методике и техническим требованиям современных веб-браузеров.
Теперь подробнее разъясним суть проблемы.
Раньше при выходе из системы, когда возникала задача открытия диалогов, приходилось писать программный код отдельно для веб-клиента и отдельно для других клиентов. Связано это с тем, что все современные веб-браузеры, если страница не хочет, чтобы ее закрывали, выводят свой диалог. Примеры этих диалогов вы сможете увидеть ниже, в той части статьи, где подробно рассматривается поведение веб-браузеров.
Что же сделали разработчики платформы? Теперь платформа ведет себя так же, как и веб-браузеры: если приложение не хочет, чтобы его закрывали, тогда выводится платформенный диалог. В результате нет необходимости писать код под разные клиенты, так как работает он одинаково, но платой за такую универсальность являются описанные выше ограничения.
При этом даже если мы не предполагаем использование веб-клиента в нашей базе, то это ничего не меняет. Ограничения все равно действуют. В некоторых случаях это может быть неудобным, но с этим придется мириться. В частности, теперь открытие дополнительных обработок при закрытии приложения может вызвать затруднение. Решение подобной задачи мы рассмотрим в конце статьи.
На этом завершаем краткий обзор изменений и переходим к подробному разбору.
Вопрос при закрытии формы
Изменения в коде будем рассматривать в сравнении с тем, как это делалось в предыдущих версиях платформы.
Ранее, когда в процессе закрытия формы возникала необходимость задать вопрос пользователю, использовался следующий программный код:
&НаКлиенте Перем ЗакрытиеРазрешено; &НаКлиенте Процедура ПередЗакрытием(Отказ, СтандартнаяОбработка) Если ЗакрытиеРазрешено=Неопределено Тогда ПоказатьВопрос( Новый ОписаниеОповещения("ПередЗакрытиемЗавершение", ЭтотОбъект), "Текст вопроса", РежимДиалогаВопрос.ДаНет); Отказ = Истина; КонецЕсли; КонецПроцедуры &НаКлиенте Процедура ПередЗакрытиемЗавершение(РезультатВопроса, ДополнительныеПараметры) Экспорт Если РезультатВопроса = КодВозвратаДиалога.Да Тогда ЗакрытиеРазрешено = Истина; Закрыть(); КонецЕсли; КонецПроцедуры
Если данный код попытаться выполнить на платформе 8.3.8 с отключенным режимом совместимости, тогда возникнет ошибка:
Рисунок 1
Теперь, начиная с версии платформы 8.3.8, необходимо выделять 2 сценария закрытия формы. Первый вариант – когда пользователь закрывает форму самостоятельно и второй вариант – когда форма закрывается в результате закрытия всего приложения.
Таким образом, код требует модернизации:
&НаКлиенте Перем ЗакрытиеРазрешено; &НаКлиенте Процедура ПередЗакрытием(Отказ, ЗавершениеРаботы, ТекстПредупреждения, СтандартнаяОбработка) Если ЗавершениеРаботы Тогда ТекстПредупреждения = "Текст"; Отказ = Истина; ИначеЕсли ЗакрытиеРазрешено=Неопределено Тогда ПоказатьВопрос( Новый ОписаниеОповещения("ПередЗакрытиемЗавершение", ЭтотОбъект), "Текст вопроса", РежимДиалогаВопрос.ДаНет); Отказ = Истина; КонецЕсли; КонецПроцедуры &НаКлиенте Процедура ПередЗакрытиемЗавершение(РезультатВопроса, ДополнительныеПараметры) Экспорт Если РезультатВопроса = КодВозвратаДиалога.Да Тогда ЗакрытиеРазрешено = Истина; Закрыть(); КонецЕсли; КонецПроцедуры
Как вы можете заметить, у обработчика ПередЗакрытием появились 2 новых параметра:
- Параметр ЗавершениеРаботы является булевым и позволяет определить причину закрытия формы
- Параметр ТекстПредупреждения используется только в процессе закрытия всего приложения. В этот параметр можно поместить текст вопроса, который будет отображен пользователю.
Теперь подробнее об изменении в коде. Как вы видите, появилась новая ветка алгоритма, которая выполняется в процессе завершения работы системы.
Обращаем ваше внимание, что никаких диалогов в этом случае не открывается, так как они запрещены. В коде устанавливается только текст предупреждения и обязательно устанавливается флаг Отказ. Как уже отмечалось выше, нельзя программным способом отменить процесс закрытия приложения, поэтому флаг Отказ работает иначе, чем при обычном закрытии формы. Установка флага указывает необходимость отображения предупреждения для пользователя, которое хранится в параметре ТекстПредупреждения.
В толстом и тонком клиенте окно предупреждения будет следующим:
Рисунок 2
Стоит отметить, что если флаг Отказ сброшен, а ТекстПредупреждения заполнен, тогда система проигнорирует текст и сообщение отображено не будет. Если же флаг Отказ установлен, а параметр ТекстПредупреждения не заполнен, тогда будет отображено системное предупреждение:
Рисунок 3
Также следует отметить, что количество подобных диалогов может быть любым – по числу закрываемых форм. Кроме того, аналогичный диалог может формироваться в обработчике ПередЗавершениемРаботыСистемы модуля управляемого приложения (этот вариант будет рассмотрен ниже). В итоге, если пользователь хотя бы в одном диалоге нажмет кнопку Продолжить работу, процесс завершения работы будет отменен и на экране останется та форма, в которой была нажата кнопка.
В веб-клиенте диалог будет выглядеть иначе. В веб-браузерах Mozilla Firefox и Google Chrome будет отображен только текст, заданный разработчиками веб-браузера и не будет отображаться текст, установленный в обработчиках событий системы.
Пример диалога для Mozilla Firefox:
Рисунок 4
Пример диалога для Google Chrome:
Рисунок 5
В веб-браузере Internet Explorer диалог будет выглядеть иначе. Будет отображен единый диалог, содержащий все сообщения, установленные в обработчиках формы и модуле управляемого приложения:
Рисунок 6
Стоит отметить, что если в свойствах конфигурации включен режим совместимости с 8.3.7 и более ранними версиями, то данный механизм будет работать в старом варианте.
Таким образом, при отключении режима совместимости следует проанализировать и скорректировать программный код по закрытию форм, так как если код содержит обращения к серверу или код по открытию дополнительных форм, то это будет приводить к ошибкам в процессе завершения работы системы.
Также можно сформулировать небольшую рекомендацию. Если в коде обработчика формы ПередЗакрытием, исполняемом в процессе завершения работы системы, необходимо получать дополнительные данные с сервера, то их следует получить заранее и хранить в данных формы.
Вопрос при закрытии приложения
В данном случае мы не будем приводить программный код, который использовался в предыдущих версиях для отображения вопроса при выходе из системы, так как он достаточно громоздкий. Приведем лишь новый код модуля управляемого приложения, который упростился до минимума:
Процедура ПередЗавершениемРаботыСистемы(Отказ, ТекстПредупреждения) ТекстПредупреждения = "Завершить работу с программой?"; Отказ = Истина; КонецПроцедуры
Теперь для отображения вопроса при выходе из системы достаточно установить текст предупреждения и флаг Отказ равным Истина.
Опять же, в коде запрещены обращения к серверу и открытия дополнительных форм, а также поведение в веб-браузере будет аналогичным тому, что уже было описано выше.
Открытие формы обработки при закрытии приложения
В завершении статьи хотелось бы продемонстрировать прием, который позволяет открыть форму некоторой обработки в процессе завершения работы системы.
Наша задача заключается в том, чтобы отменить процесс закрытия приложения и открыть форму некоторой обработки. Проблема в том, что отменить процесс закрытия или открыть форму мы не можем, так как это запрещено.
Поэтому, для того чтобы отменить закрытие приложения, потребуется использовать предупреждение, а для того чтобы открыть форму, используем обработчик ожидания, который выполнится только в том случае, если пользователь откажется от завершения работы системы.
Пример кода:
Процедура ПередЗавершениемРаботыСистемы(Отказ, ТекстПредупреждения) #Если ВебКлиент Тогда ТекстПредупреждения = "Завершить работу с программой?"; #Иначе ТекстПредупреждения = "Для запуска обработки проведения документов нажмите ""Продолжить работу"""; ПодключитьОбработчикОжидания("ОткрытиеОбработки", 0.1, Истина); #КонецЕсли Отказ = Истина; КонецПроцедуры Процедура ОткрытиеОбработки() Экспорт ОткрытьФорму("Обработка.ПроведениеДокументов.Форма"); КонецПроцедуры
Используя инструкции препроцессора, мы разделили сценарии закрытия в веб-клиенте и в других видах клиентов. В случае веб-клиента мы не открываем обработку, так как практически во всех веб-браузерах текст нашего предупреждения не отображается. В других видах клиентов будет отображен наш диалог:
Рисунок 7
Если пользователь нажмет кнопку Продолжить работу, тогда сработает обработчик ожидания и будет открыта форма обработки.
Заключение
Платформа продолжает свое уверенное движение в сторону веб-браузеров, но на этот раз разработчики наложили ограничения на программный код, исполняемый в толстом и тонком клиентах. С одной стороны, это упростило и унифицировало код при закрытии форм, так как теперь нет необходимости писать несколько вариантов кода под каждый клиент, с другой стороны, это ограничило нас в возможностях.
Об авторе
PDF-версия статьи для участников группы ВКонтакте
Мы ведем группу ВКонтакте – http://vk.com/kursypo1c.
Если Вы еще не вступили в нее – сделайте это сейчас, и в блоке ниже (на этой странице) появятся ссылки на скачивание материалов.
Если Вы уже участник группы – нужно просто повторно авторизоваться в ВКонтакте, чтобы скрипт Вас узнал. В случае проблем решение стандартное: очистить кэш браузера или подписаться через другой браузер.