Закрытие модуля в 1С: окно, форма, транзакции и ресурсы

· 9 мин

Содержание

Закрыть модуль в 1С — это не одна кнопка. В Конфигураторе модуль закрывают как окно (Ctrl+F4), но программисты чаще спрашивают о другом: как корректно завершить работу кода модуля. Закрыть форму из её модуля, не оставить открытую транзакцию, освободить файл и COM-объект, обработать выход пользователя из программы.

Частое заблуждение — считать, что платформа «сама всё закроет». Окно она закроет. А вот незакрытый ЗаписьТекста держит файл, Excel висит в диспетчере задач, а транзакция с ошибкой ломает всю запись. Разберём каждый случай с кодом для 1С:Предприятия 8.3 на управляемых формах.

Четыре смысла фразы «закрыть модуль»

Прежде чем искать решение, уточните, что именно нужно закрыть. Под одним запросом прячутся разные задачи:

  • 🪟 Окно редактора модуля в Конфигураторе или 1С:EDT — с сохранением правок или без.
  • 📋 Форму из модуля формы — программно, по кнопке или по условию, с вопросом пользователю.
  • 🔒 Транзакцию и блокировки, открытые кодом модуля, — зафиксировать или отменить.
  • 📂 Внешние ресурсы: файлы, XML/JSON-потоки, COM-объекты, менеджер временных таблиц.

Отдельно стоят события завершения сеанса — модуль управляемого приложения. И работа с хранилищем конфигурации, где «закрыть» модуль значит вернуть захват.

Что закрываемИнструментКто отвечает
Окно модуляCtrl+F4, крестик вкладкиРазработчик в Конфигураторе
Формуметод Закрыть()Код модуля формы
ТранзакциюЗафиксироватьТранзакцию() / ОтменитьТранзакцию()Код, открывший транзакцию
Файл, потокметод Закрыть() объектаКод, создавший объект
COM-объектметод приложения + = НеопределеноКод, создавший объект

Как закрыть окно модуля в Конфигураторе и EDT

Конфигуратор

  1. Проверьте синтаксис: Ctrl+F7 («Проверить модуль»). Ошибки покажутся в окне сообщений внизу.
  2. Сохраните конфигурацию: Ctrl+S. Правки попадут в основную конфигурацию, но ещё не в базу данных.
  3. Закройте окно модуля: Ctrl+F4 или крестик на вкладке окна.
  4. Когда закончите правки, обновите конфигурацию базы данных: F7.

Если закрыть окно без сохранения, Конфигуратор не спросит про каждый модуль отдельно. Изменения останутся в открытой конфигурации до её сохранения или закрытия Конфигуратора. При выходе программа предложит сохранить.

Открыто два десятка модулей и тесно? Закройте лишние через меню Окна — там есть команда закрытия всех окон. Несохранённые правки при этом не теряются: они в памяти конфигурации, а не окна.

Конфигурация в хранилище

Если база подключена к хранилищу, модуль нельзя править без захвата объекта. «Закрыть» здесь значит вернуть объект:

  • ✅ Поместить в хранилище — правки уходят коллегам, захват снимается.
  • ↩️ Отменить захват — правки откатываются к версии хранилища.

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

1С:EDT

В EDT редактор модуля — обычная вкладка Eclipse. Ctrl+W закрывает текущую, Ctrl+Shift+W — все. Несохранённые вкладки помечены звёздочкой, перед закрытием среда спросит про них. Сохранение — Ctrl+S, в информационную базу правки уходят отдельным обновлением.

Закрываем форму из её модуля

Форма закрывается методом Закрыть(). Его вызывают на клиенте, из любого клиентского обработчика модуля формы:

&НаКлиенте
Процедура КомандаГотово(Команда)
	Закрыть(Объект.Ссылка);
КонецПроцедуры

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

С сервера закрыть форму нельзя. Если решение «закрывать или нет» принимается на сервере, верните признак в клиентскую процедуру и вызовите Закрыть() уже там.

Вопрос «Сохранить изменения?» без модальности

Классическая ловушка — Вопрос() в обработчике ПередЗакрытием. В режиме без модальности это даёт ошибку Использование модальных окон в данном режиме запрещено. Правильный шаблон: отменить закрытие, задать вопрос асинхронно и закрыть форму в обработчике ответа.

&НаКлиенте
Перем ЗакрытиеПодтверждено;

&НаКлиенте
Процедура ПередЗакрытием(Отказ, ЗавершениеРаботы, ТекстПредупреждения, СтандартнаяОбработка)

	Если Не Модифицированность Или ЗакрытиеПодтверждено = Истина Тогда
		Возврат;
	КонецЕсли;

	Отказ = Истина;

	Если ЗавершениеРаботы Тогда
		// При выходе из программы диалоги показывать нельзя
		ТекстПредупреждения = "В форме есть несохранённые изменения.";
		Возврат;
	КонецЕсли;

	Оповещение = Новый ОписаниеОповещения("ПослеОтветаНаВопрос", ЭтотОбъект);
	ПоказатьВопрос(Оповещение, "Закрыть форму без сохранения?", РежимДиалогаВопрос.ДаНет);

КонецПроцедуры

&НаКлиенте
Процедура ПослеОтветаНаВопрос(Ответ, ДополнительныеПараметры) Экспорт

	Если Ответ = КодВозвратаДиалога.Да Тогда
		ЗакрытиеПодтверждено = Истина;
		Закрыть();
	КонецЕсли;

КонецПроцедуры

Флаг ЗакрытиеПодтверждено нужен, чтобы повторный вызов Закрыть() не попал в тот же вопрос по кругу. Сравнение = Истина защищает от значения Неопределено до первого ответа.

Параметр ЗавершениеРаботы равен Истина, когда пользователь выходит из программы целиком. В этот момент никаких диалогов: только ТекстПредупреждения, который платформа покажет в общем списке предупреждений.

ПередЗакрытием и ПриЗакрытии — в чём разница

ОбработчикКогда вызываетсяМожно отменить
ПередЗакрытиемДо закрытия, форма ещё на экранеДа, Отказ = Истина
ПриЗакрытииФорма уже закрываетсяНет

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

Транзакции и блокировки: закрыть до выхода из процедуры

Транзакция, начатая в модуле, должна закончиться в нём же — фиксацией или отменой. Безопасный шаблон:

НачатьТранзакцию();
Попытка

	Блокировка = Новый БлокировкаДанных;
	ЭлементБлокировки = Блокировка.Добавить("РегистрНакопления.ИмяРегистра");
	ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
	Блокировка.Заблокировать();

	// чтение остатков и запись данных

	ЗафиксироватьТранзакцию();

Исключение
	ОтменитьТранзакцию();
	ВызватьИсключение;
КонецПопытки;

Правила, которые держат этот код в порядке:

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

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

Вложенные транзакции

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

Внутри транзакции не показывайте сообщений с ожиданием ответа и не обращайтесь к внешним сервисам. Пока ждём HTTP-ответ, блокировка висит и тормозит коллег.

Освобождение файлов, потоков, COM-объектов и кэша

Файлы и потоки

Объекты ЗаписьТекста, ЧтениеТекста, ЗаписьXML, ЧтениеXML, ЗаписьJSON, ЧтениеJSON держат файл открытым до вызова Закрыть(). Закрывайте и в ветке ошибки:

Запись = Новый ЗаписьТекста(ИмяФайла, КодировкаТекста.UTF8);
Попытка
	Для Каждого Строка Из МассивСтрок Цикл
		Запись.ЗаписатьСтроку(Строка);
	КонецЦикла;
	Запись.Закрыть();
Исключение
	Запись.Закрыть();
	ВызватьИсключение;
КонецПопытки;

Без Закрыть() последние строки могут не попасть на диск, а файл останется занят. Следующая попытка его удалить или переписать упадёт.

Временные файлы удаляйте явно: УдалитьФайлы(ИмяВременногоФайла). Имя берите через ПолучитьИмяВременногоФайла("xml"), чтобы не путаться с чужими файлами.

COM-объекты

Excel, Word и другие приложения через COM не закрываются при выходе из процедуры:

Excel = Новый COMОбъект("Excel.Application");
Попытка
	Книга = Excel.Workbooks.Open(ИмяФайла);
	// чтение ячеек
	Книга.Close(Ложь);
Исключение
	Excel.Quit();
	ВызватьИсключение;
КонецПопытки;
Excel.Quit();
Книга = Неопределено;
Excel = Неопределено;

Quit() закрывает приложение, присвоение Неопределено отпускает ссылку. Без этого после каждой загрузки в диспетчере задач копится процесс EXCEL.EXE.

Временные таблицы и временное хранилище

  • 🗃️ МенеджерВременныхТаблиц закрывайте методом Закрыть(), когда таблицы больше не нужны. На больших выборках это заметно разгружает сервер СУБД.
  • 📦 Данные, помещённые в ПоместитьВоВременноеХранилище с адресом формы, живут до её закрытия. Если адрес временный, удаляйте явно: УдалитьИзВременногоХранилища(Адрес).

Кэш общих модулей

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

ОбновитьПовторноИспользуемыеЗначения();

Метод вызывают на клиенте или сервере. Он сбрасывает кэш именно этой стороны, поэтому при необходимости вызывайте его в обоих контекстах.

Завершение работы в разных режимах и клиентах

Модуль, который закрывается вместе с сеансом, — модуль управляемого приложения. В нём два обработчика:

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

Как ведёт себя закрытие в разных режимах:

РежимОсобенность
Тонкий клиентОбработчики срабатывают при выходе через меню и крестик главного окна
Веб-клиентЗакрытие вкладки браузера — особый случай: браузер даёт лишь стандартное «Покинуть страницу?», долгий код не успеет выполниться
Толстый клиент, обычные формыИспользуется модуль обычного приложения, разрешены модальные вызовы, если конфигурация их допускает
Фоновое заданиеФорм и клиентских событий нет; ресурсы закрывайте в коде самого задания
Внешняя обработкаЗакрытие её формы — обычные ПередЗакрытием и ПриЗакрытии

Модуль сеанса и общие серверные модули событий закрытия не имеют. Всё, что они открыли, закрывается в той же процедуре.

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

Автоматизация: закрытие по таймеру и массовый выход пользователей

Закрыть форму по времени

Форму, которую забывают закрыть (монитор, табло, окно ожидания), закрывают обработчиком ожидания:

&НаКлиенте
Процедура ПриОткрытии(Отказ)
	ПодключитьОбработчикОжидания("ЗакрытьПоТаймеру", 600, Истина);
КонецПроцедуры

&НаКлиенте
Процедура ЗакрытьПоТаймеру() Экспорт
	Закрыть();
КонецПроцедуры

&НаКлиенте
Процедура ПриЗакрытии(ЗавершениеРаботы)
	ОтключитьОбработчикОжидания("ЗакрытьПоТаймеру");
КонецПроцедуры

Третий параметр Истина — однократный вызов: через 600 секунд, то есть 10 минут, форма закроется один раз. Обработчик должен быть экспортной процедурой модуля формы.

Выгнать пользователей перед обновлением

Чтобы закрыть все сеансы перед правкой модулей и обновлением структуры, в типовых конфигурациях на БСП есть функция блокировки работы пользователей в разделе Администрирование. Она выставляет время блокировки, рассылает предупреждение и завершает сеансы.

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

Для клиент-серверного варианта сеансы видны в консоли кластера серверов. В командной строке — rac session list; параметры завершения сеанса смотрите в rac help session.

Перед тем как закрыть модуль с правками
Выполнено: 0 / 7

Диагностика: форма не закрывается, файл занят, транзакция сломана

Диагностика: выберите симптом

Чем искать:

  1. Отладчик. Точка останова в ПередЗакрытием или перед ЗафиксироватьТранзакцию() покажет, по какой ветке идёт код.
  2. ТранзакцияАктивна(). Вычислите выражение в отладчике на выходе из процедуры. Истина там, где транзакции быть не должно, — значит, где-то ранний Возврат.
  3. Парные операторы. В редакторе Конфигуратора и EDT можно сворачивать группы кода. Свернули процедуру — и сразу видно, где блок не закрылся.
  4. Технологический журнал. Для серверных случаев (долгие блокировки, зависшие транзакции) его включают через файл logcfg.xml на сервере. Нужны права администратора сервера.

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

Нужно ли закрывать модуль объекта или менеджера?
Нет. Такие модули не открываются и не закрываются как формы. Закрывать нужно то, что открыл их код: транзакции, файлы, COM-объекты, временные таблицы.
Можно ли закрыть форму с сервера?
Напрямую нет: Закрыть() работает только на клиенте. Верните с сервера признак, а форму закройте в клиентской процедуре.
Почему ПриЗакрытии не срабатывает при закрытии вкладки браузера?
Браузер не даёт странице выполнять долгий код при закрытии. Важные действия делайте явно — по кнопке «Записать и закрыть», а не в событии закрытия.
Чем Закрыть() отличается от ЗавершитьРаботуСистемы()?
Закрыть() закрывает одну форму. ЗавершитьРаботуСистемы() завершает весь сеанс пользователя со всеми открытыми окнами.
Снимаются ли блокировки, если закрыть 1С во время транзакции?
Да. При обрыве сеанса незафиксированная транзакция откатывается, управляемые блокировки снимаются вместе с ней.