Программное завершение сеанса 1С: рабочие способы и мифы

· 9 мин

Содержание

Закрыть клиентское приложение из встроенного языка позволяет одна глобальная процедура — ЗавершитьРаботуСистемы(). Она завершает текущий сеанс 1С:Предприятие программно, при желании с перезапуском клиента. Всё остальное — закрытие форм, перехват исключений, снятие процесса — решает другие задачи, и путать их с выходом из программы не стоит.

Разберём, чем завершение сеанса отличается от закрытия формы и от исключения, как завершить работу тихо или с вопросом пользователю, как перехватить закрытие, что делать в веб-клиенте и как администратор снимает чужой зависший сеанс на сервере.

Глобальная процедура для выхода из программы

Корректный выход из клиента — это вызов:

// Завершить текущий сеанс без вопроса о сохранении
ЗавершитьРаботуСистемы(Ложь);

Процедура доступна только на стороне клиента — в модуле формы, в модуле команды, в клиентской процедуре общего модуля. Из серверного кода (регламентное задание, &НаСервере) закрыть клиентский сеанс нельзя: сервер не управляет окнами пользователя. Для завершения чужих сеансов используют административные инструменты — о них ниже.

В актуальных версиях платформы процедура принимает два необязательных параметра:

ПараметрЧто делает
Первый (С вопросом)Истина — платформа покажет стандартное предупреждение о несохранённых данных; Ложь — завершит сразу
Второй (Перезапустить)Истина — после выхода клиент запустится заново

Тихое завершение без диалогов:

ЗавершитьРаботуСистемы(Ложь, Ложь);

Два типовых сценария, ради которых код закрывает сеанс:

  1. После смены региональных или пользовательских настроек, которые платформа читает только при старте. Перезапуск применяет их без ручного переоткрытия:
СохранитьНастройкиПользователя();
ЗавершитьРаботуСистемы(Ложь, Истина); // выход с перезапуском
  1. Принудительный выход перед обновлением. Здесь важно не «убить» сеанс, а дать клиенту закрыться штатно, предупредив человека и оставив шанс сохранить данные:
Сообщить("База закрывается на обновление. Сохраните данные.");
ЗавершитьРаботуСистемы(Истина); // с вопросом о сохранении

Насчёт сохранности данных: всё, что было записано и зафиксировано до выхода, в базе остаётся. А вот незавершённая транзакция (между НачатьТранзакцию() и ЗафиксироватьТранзакцию()) при закрытии сеанса откатывается платформой целиком — наполовину записанных данных не бывает. Поэтому длинные операции с записью лучше доводить до конца, а не прерывать выходом из программы.

Ставьте вызов ЗавершитьРаботуСистемы() последней строкой процедуры. Платформа завершает текущий клиентский вызов, и код после этой строки всё ещё может отработать — не рассчитывайте, что выполнение прервётся мгновенно.

В старых конфигурациях на обычном приложении (толстый клиент без управляемых форм) за выход отвечала процедура ПрекратитьРаботуСистемы() без параметров. В управляемом приложении 8.3 она не применяется — используйте ЗавершитьРаботуСистемы().

Перехват и отмена закрытия сеанса

Выход из программы можно не только инициировать, но и перехватить. За это отвечают два события в модуле управляемого приложения (МодульУправляемогоПриложения), которые срабатывают независимо от того, кто инициировал выход — пользователь кнопкой или ваш код.

Перед завершением вызывается обработчик, в котором ещё можно отменить выход:

Процедура ПередЗавершениемРаботыСистемы(Отказ, ТекстПредупреждения)
    Если ЕстьНезавершённыеОперации() Тогда
        Отказ = Истина; // выход не состоится
        ТекстПредупреждения = "Завершите текущую операцию перед выходом";
    КонецЕсли;
КонецПроцедуры

Установка Отказ = Истина блокирует закрытие — удобно, когда нельзя дать пользователю выйти с незаписанными документами или незавершённой обработкой.

При завершении вызывается обработчик для финального кода — здесь отменить выход уже нельзя, зато можно выполнить уборку: сбросить временные данные, записать в журнал регистрации, закрыть внешнее соединение:

Процедура ПриЗавершенииРаботыСистемы()
    ОчиститьВременныеФайлыСеанса();
    ЗаписьЖурналаРегистрации("Сеанс.Завершение",
        УровеньЖурналаРегистрации.Информация, , ,
        "Пользователь завершил работу");
КонецПроцедуры

Запись в журнал регистрации при выходе помогает потом разобраться, кто и когда закрыл базу, — особенно если выход инициирует код, а не человек.

Не затягивайте код в этих обработчиках. Долгие операции при закрытии раздражают пользователя и могут выглядеть как зависание. Тяжёлую фоновую работу выносите в регламентные задания на сервере, а не в момент выхода из клиента.

Оба обработчика живут в модуле управляемого приложения. Открыть его можно из конфигуратора: в дереве конфигурации вызовите контекстное меню на корневом узле и выберите открытие модуля приложения. Если обработчиков там ещё нет, их добавляют вручную по именам событий.

Эти два события — штатный механизм контроля выхода. Если задача звучит как «не дать закрыть программу, пока…» — решается она именно здесь, а не исключениями.

Почему через исключение сеанс не закрывается

Частое заблуждение: «выбросим исключение — и программа закроется». Это не так. Исключение не завершает работу 1С, оно лишь прерывает текущую операцию.

Конструкция Попытка … Исключение … КонецПопытки ловит ошибку, а оператор ВызватьИсключение её порождает. Управление при этом поднимается по стеку вызовов до ближайшего обработчика или до платформы, которая покажет пользователю сообщение об ошибке. Сам сеанс остаётся открытым.

Попытка
    ПровестиДокумент();
Исключение
    Сообщить("Не удалось провести: " + ОписаниеОшибки());
КонецПопытки;

Если задача была не «закрыть программу», а отменить действие (запись, проведение, открытие формы), то нужен не выход из системы, а отказ от операции. В обработчиках событий для этого есть параметр Отказ:

&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ТекущийОбъект)
    Если ТекущийОбъект.Сумма = 0 Тогда
        Отказ = Истина; // запись не состоится
    КонецЕсли;
КонецПроцедуры

Выброс исключения в таком обработчике тоже отменит запись, но вдобавок покажет пользователю красное окно ошибки. Для штатной отмены чаще корректнее установить Отказ = Истина и при необходимости вывести понятное сообщение через Сообщить().

Вывод простой: исключение — инструмент обработки ошибок, а не способ выйти из приложения. Для выхода есть ЗавершитьРаботуСистемы().

Закрытие форм и окон перед выходом

Отдельной глобальной команды вроде «очистить все формы» в платформе нет. Закрытие окна и завершение сеанса — разные операции.

Закрыть текущую управляемую форму:

ЭтаФорма.Закрыть();

Вернуть значение в вызвавшую форму и закрыться (типично для форм выбора):

ОповеститьОВыбореИЗакрыть(ВыбранныйЭлемент);

Когда вы вызываете ЗавершитьРаботуСистемы(), платформа сама пытается закрыть все открытые формы. Если в каком-то окне есть несохранённые данные, а первый параметр процедуры равен Истина, пользователь получит стандартный вопрос о сохранении. При Ложь система закроет окна без вопросов — несохранённое при этом потеряется.

Тихое завершение (ЗавершитьРаботуСистемы(Ложь)) не спрашивает про несохранённые данные. Применяйте его, только когда уверены, что ничего важного в открытых формах не осталось, или сначала сами сохраните нужные объекты в коде.

Если задача — закрыть одно окно и оставить программу работать, вызывайте Закрыть() у нужной формы, а не завершение всей системы.

Особенности веб-клиента и тонкого клиента

Процедура ЗавершитьРаботуСистемы() работает во всех клиентах — толстом, тонком и веб. Но поведение различается:

  • Тонкий клиент (1cv8c) и толстый клиент (1cv8) закрывают приложение полностью. С Перезапустить = Истина запускается новый экземпляр.
  • Веб-клиент работает внутри вкладки браузера. Платформа завершает сеанс 1С, но закрыть саму вкладку браузер по соображениям безопасности может запретить — это ограничение браузера, а не 1С. С перезапуском веб-клиент перезагружает страницу приложения.

Не рассчитывайте, что в веб-клиенте код гарантированно закроет вкладку. Логика приложения не должна зависеть от того, исчезло окно браузера или нет — ориентируйтесь на завершение самого сеанса.

Остальная логика (параметры процедуры, события завершения, отмена через Отказ, закрытие форм через Закрыть()) в веб-клиенте идентична обычному клиенту.

Аварийное завершение процесса в операционной системе

Когда клиент завис и не реагирует, встроенный язык уже не поможет — код в зависшем сеансе не выполняется. Остаётся снять процесс средствами ОС.

На Windows клиент — это процесс 1cv8 (толстый) или 1cv8c (тонкий):

taskkill /F /IM 1cv8.exe

Если открыто несколько клиентов, а завис только один, снимайте по идентификатору процесса (PID), а не все сразу по имени — PID виден в Диспетчере задач на вкладке с подробностями. На Linux клиент тоже называется 1cv8, зависший процесс снимают командой kill по его PID (его показывает ps или системный монитор). Принудительное снятие на обеих системах — одинаково грубая операция с одним и тем же риском потери данных.

Снятие процесса — аварийный метод. Несохранённые данные в открытых формах пропадут. Для файловой базы незакрытый сеанс может оставить блокировку (файл 1Cv8.1CL) — после снятия процесса убедитесь, что в базу больше никто не вошёл, прежде чем запускать её снова. Делайте это только с конкретным зависшим клиентом, а не со всеми процессами подряд.

Снимать серверные процессы (rphost, ragent) на боевом сервере без необходимости нельзя: оборвутся все сеансы пользователей этого кластера. Это задача администратора в согласованное окно, а не способ «закрыть 1С».

Как администратор завершает чужой сеанс

Если нужно выкинуть из базы конкретного пользователя (например, тот забыл выйти перед обновлением), сеанс завершают на стороне сервера:

  • Консоль администрирования серверов 1С — в ветке кластера откройте список сеансов, найдите нужный по имени пользователя и удалите его.
  • Утилита rac из командной строки сервера:
rac session list --cluster=<идентификатор кластера>
rac session terminate --cluster=<идентификатор кластера> --session=<идентификатор сеанса>

Идентификаторы берутся из вывода rac session list. Полный перечень параметров смотрите в rac help — не подставляйте ключи наугад.

Если нужно выгнать сразу всех перед обновлением, аккуратнее не завершать сеансы по одному, а включить в консоли кластера блокировку начала сеансов: новые пользователи не войдут, а текущих можно корректно попросить выйти. Это мягче принудительного завершения.

Завершение чужого сеанса обрывает его работу так же, как снятие процесса: несохранённые данные пользователя теряются. Предупредите человека, если это не аварийная ситуация.

Сравнение способов завершения

Что именно нужно сделать?
СпособЧто закрываетКогда применятьРиск
ЗавершитьРаботуСистемы(Ложь)Свой сеанс, тихоШтатный выход из кодаТеряются несохранённые данные открытых форм
ЗавершитьРаботуСистемы(Истина)Свой сеанс, с вопросомВыход с шансом сохранитьПользователь может отменить
ПередЗавершениемРаботыСистемыОтменяет выходЗапретить закрытие по условиюНизкий
Закрыть() у формыОдно окноЗакрыть форму, не выходяНизкий
Отказ = ИстинаНичего не закрываетОтменить запись/проведениеНизкий
Консоль кластера / racЧужой сеансВыгнать пользователя с сервераПотеря его данных
taskkill по 1cv8Процесс клиентаЗависший клиентВысокий: потеря данных, блокировки

Правило выбора короткое: для планового выхода из кода — только ЗавершитьРаботуСистемы(). Процесс и чужие сеансы снимают лишь в аварийных ситуациях и с пониманием последствий.

Частые вопросы

Можно ли закрыть 1С из серверного кода или регламентного задания?
Нет. ЗавершитьРаботуСистемы() — клиентская процедура, она управляет окном пользователя. На сервере клиентских сеансов нет. Чтобы завершить чужой сеанс с сервера, используйте консоль кластера или rac session terminate.
Как закрыть программу без вопроса о сохранении данных?
Передайте первым параметром Ложь: ЗавершитьРаботуСистемы(Ложь). Платформа закроет формы без диалога. Несохранённое при этом потеряется — сохраните нужное заранее.
Почему после `ЗавершитьРаботуСистемы()` следующая строка кода всё равно выполняется?
Процедура инициирует завершение, но текущий клиентский вызов платформа доводит до конца. Поэтому её ставят последней строкой и не рассчитывают на мгновенный обрыв.
Можно ли запретить пользователю закрывать программу?
Да, через событие ПередЗавершениемРаботыСистемы в модуле управляемого приложения: установите в нём Отказ = Истина. Пользователь увидит ваш текст предупреждения и останется в системе.
Как перезапустить клиент из кода?
Вторым параметром процедуры передайте Истина: ЗавершитьРаботуСистемы(Ложь, Истина). Толстый и тонкий клиент запустятся заново, веб-клиент перезагрузит страницу.