При работе с платформой 1С:Предприятие 7.7 и популярной внешней компонентой FormEx многие разработчики сталкиваются с неожиданным поведением. Метод ОстановитьВыполнение(), который, по логике, должен просто прекращать выполнение кода, в некоторых версиях компоненты (например, в 175-й) вместо этого генерирует ошибку времени выполнения. Разберем, почему так происходит и как это исправить.
Проанализируем ситуацию: у нас есть сложный алгоритм с множеством вложенных вызовов процедур и функций. В какой-то момент на большой глубине вложенности возникает условие, при котором дальнейшее выполнение всего стека вызовов бессмысленно. Идеальным решением было бы просто остановить все и вернуть управление пользователю, без каких-либо сообщений об ошибках. Именно для этого и предназначен метод ОстановитьВыполнение(), но вместо ожидаемого результата мы получаем аварийное завершение.
Чтобы понять корень проблемы, обратимся к деталям реализации. Как подтвердил сам автор компоненты FormEx (известный под псевдонимом АЛьФ), в старых версиях, включая 175-ю, данный метод содержал ошибку в логике. Вместо тихой остановки он вызывал внутреннюю функцию CBLModule::RaiseExtRuntimeError().
Что это значит для нас? Это означает, что метод был спроектирован так, чтобы намеренно генерировать исключение (ошибку). Такое поведение полностью противоречит названию и ожидаемому функционалу метода. По сути, это была не ошибка в вашем коде, а баг в самой компоненте.
Попытки решить проблему, например, созданием объекта "Сервис" локально в процедуре, а не использованием глобального, не приводили к успеху, так как проблема была заложена в самой реализации метода.
Решение этой проблемы, к счастью, очень простое и не требует никаких изменений в коде 1С.
Необходимо обновить компоненту FormEx до более свежей версии (например, 181 или выше).
Разработчик исправил это поведение в последующих релизах. В обновленных версиях метод ОстановитьВыполнение() работает именно так, как и должен: он корректно прерывает всю цепочку вызовов процедур и функций и возвращает управление в интерактивный режим, не генерируя никаких ошибок. Это позволяет "чисто" выходить из глубоко вложенных алгоритмов.
Чтобы лучше понять ценность метода ОстановитьВыполнение(), давайте сравним его со стандартными операторами 1С 7.7, которые управляют ходом выполнения программы.
Оператор Прервать
Этот оператор предназначен для досрочного выхода только из текущего цикла (Для...Цикл или Пока...Цикл). Он не влияет на выполнение процедуры, в которой находится цикл.
Посмотрим на пример:
Процедура ПримерПрервать()
Для Сч = 1 По 10 Цикл
Если Сч = 5 Тогда
// Выходим из цикла, но процедура продолжит выполняться
Прервать;
КонецЕсли;
Сообщить("Итерация цикла №" + Сч);
КонецЦикла;
// Этот код выполнится
Сообщить("Цикл завершен, выполнение процедуры продолжается.");
КонецПроцедуры
В результате мы увидим сообщения об итерациях с 1 по 4 и финальное сообщение о продолжении процедуры. Выполнение программы не остановится.
Оператор Возврат
Этот оператор прекращает выполнение текущей процедуры или функции. Однако код в вызывающей процедуре продолжит выполняться с того места, где был сделан вызов.
Рассмотрим пример:
Процедура ОсновнаяПроцедура()
Сообщить("Начало основной процедуры.");
ВызываемаяПроцедура();
// Этот код выполнится после того, как ВызываемаяПроцедура завершится
Сообщить("Возвратились в основную процедуру и продолжаем работу.");
КонецПроцедуры
Процедура ВызываемаяПроцедура()
Сообщить("Начало вызываемой процедуры.");
Если 1 = 1 Тогда
// Прекращаем выполнение ТОЛЬКО этой процедуры
Возврат;
КонецЕсли;
// Этот код никогда не выполнится
Сообщить("Этот текст не будет выведен.");
КонецПроцедуры
Здесь Возврат лишь вернет управление в ОсновнаяПроцедура, которая продолжит свою работу.
Формекс.ОстановитьВыполнение()Стандартные средства не позволяют прервать всю цепочку вызовов сразу. Без FormEx для решения такой задачи пришлось бы использовать "флаги" — специальные переменные, которые нужно проверять на каждом уровне вложенности. Это сильно усложняет и загрязняет код.
Пример "плохого" кода с флагами:
Перем глНужноОстановить Экспорт;
Процедура КнопкаВыполнить()
глНужноОстановить = 0;
Процедура1();
Если глНужноОстановить = 1 Тогда
Возврат;
КонецЕсли;
// ... какой-то код
КонецПроцедуры
Процедура Процедура1()
Процедура2();
Если глНужноОстановить = 1 Тогда
Возврат;
КонецЕсли;
// ... какой-то код
КонецПроцедуры
Процедура Процедура2()
Если ВыполненоУсловиеДляОстанова() = 1 Тогда
глНужноОстановить = 1;
Возврат;
КонецЕсли;
// ... какой-то код
КонецПроцедуры
Как видим, код становится громоздким из-за постоянных проверок. А теперь посмотрим, как элегантно эта же задача решается с помощью обновленного FormEx.
Пример "хорошего" кода с ОстановитьВыполнение():
Процедура КнопкаВыполнить()
Процедура1();
// Если в Процедуре2 произойдет останов, сюда выполнение уже не вернется
Сообщить("Все процедуры успешно выполнены.");
КонецПроцедуры
Процедура Процедура1()
Процедура2();
// Никаких проверок не нужно
КонецПроцедуры
Процедура Процедура2()
Если ВыполненоУсловиеДляОстанова() = 1 Тогда
// Просто останавливаем всё!
Формекс = СоздатьОбъект("AddIn.FormEx");
Формекс.ОстановитьВыполнение();
КонецЕсли;
// ... какой-то код
КонецПроцедуры
Код становится чище, проще для чтения и поддержки. Один вызов метода решает задачу, для которой раньше требовалось городить сложную логику с переменными-флагами.
Итог: если вы столкнулись с ошибкой при вызове ОстановитьВыполнение(), не ищите проблему в своем коде. Просто скачайте и установите последнюю версию компоненты FormEx, и метод заработает как положено, став мощным инструментом в вашем арсенале разработчика 1С 7.7.