При разработке в среде 1С:Предприятие 8.3 программисты часто сталкиваются с загадочным поведением платформы: код в расширении начинает выполняться повторно, «прыгает» на начало процедуры или открывает одну и ту же форму дважды. Это создает иллюзию бесконечного цикла или ошибки в логике программы, хотя визуально код выглядит абсолютно корректным. В этой статье мы подробно разберем, почему возникают такие ситуации, как проанализировать стек вызовов и как правильно исправлять подобные ошибки в расширениях.
Рассмотрим типичную ситуацию. Программист создает расширение для документа и добавляет команду, которая должна открывать форму элемента справочника (например, для прикрепления файлов). Используется стандартная аннотация &НаКлиенте и метод ОткрытьФорму. Однако при нажатии на кнопку форма открывается два раза, а отладчик показывает, что после завершения процедуры управление снова передается на ее первую строку.
Пример кода, который может вызывать подобное поведение:
&НаКлиенте
Процедура Вложения_ПрикрепитьФайлПосле(Команда)
П = Новый Структура;
П.Вставить("Основание", Объект.Ссылка);
ОткрытьФорму("Справочник.ПрикрепленныеФайлы.Форма.ФормаЭлемента", П, ЭтотОбъект, ЭтаФорма.УникальныйИдентификатор);
КонецПроцедуры
Казалось бы, здесь нет условий или циклов, которые могли бы спровоцировать повторный вызов. Выясним причину, скрытую глубоко в механизмах работы платформы с метаданными.
Первым делом нам необходимо понять, кто именно инициирует повторный вызов. Для этого в 1С существует мощный инструмент — Стек вызовов. Разберем по шагам, как его использовать для локализации проблемы:
Вложения_ПрикрепитьФайлПосле встречается в списке несколько раз или вызывается из неожиданных мест (например, из системных обработчиков событий), значит, мы имеем дело с дублированием событий.Проанализируем пример реального стека вызовов из подобной ситуации:
Документ.Расш2_Дело.Форма.ФормаДокумента.Форма.Вложения_ПрикрепитьФайлПосле(Команда = КомандаФормы)
Справочник.ПрикрепленныеФайлы.Форма.ФормаЭлемента.Форма.ПриОткрытии(Отказ = Ложь)
Документ.Расш2_Дело.Форма.ФормаДокумента.Форма.Вложения_ПрикрепитьФайлПосле(Команда = КомандаФормы)
Такой стек явно указывает на то, что вызов зацикливается или дублируется на уровне взаимодействия форм. Обратите внимание на строки, где фигурирует КомандаФормы. Это важный сигнал о том, что проблема может быть связана именно с объектом команды в метаданных.
Как показывает практика, наиболее частая причина «самопроизвольного» перехода на начало процедуры — это копирование объектов метаданных в расширении. Рассмотрим, что происходит «под капотом»:
Когда вы копируете команду из другого расширения или из основной конфигурации, платформа может перенести вместе с ней внутренние идентификаторы (GUID). Если в расширении оказывается две кнопки или две команды, которые платформа считает идентичными из-за конфликта идентификаторов, она может попытаться выполнить действие для каждой из них. Или, что еще хуже, событие нажатия кнопки связывается одновременно и с обработчиком команды, и с обработчиком события кнопки.
Решение проблемы:
Никогда не используйте копирование (Ctrl+C / Ctrl+V) для создания команд в расширениях, если они должны управлять логикой формы. Вместо этого:
ИмяКоманды выберите только что созданную команду.Это гарантирует, что в XML-структуре расширения будет создан чистый объект с уникальным идентификатором, и платформа не будет путаться при сопоставлении действий пользователя и программного кода.
Разберем особенности работы расширений. Когда мы используем аннотацию &После, платформа выполняет сначала основной код процедуры из конфигурации поставщика, а затем — наш код из расширения.
Если в основной конфигурации уже прописан вызов ОткрытьФорму, а вы в расширении через &После добавляете еще один вызов ОткрытьФорму, вы получите две открытые формы. Но бывает и сложнее: если основной код выполняет действия, которые меняют фокус или состояние формы, вызов из расширения может спровоцировать повторную инициализацию событий формы (например, ПриАктивизации или ПриОбновленииОтображения), что визуально выглядит как возврат в начало логики.
Проанализируем ситуацию, когда обработчик назначен кнопке дважды. В свойствах элемента формы типа «Кнопка» есть два важных поля:
Если вы прописали код в команде, а затем еще раз выбрали ту же процедуру в событии «Нажатие», платформа выполнит код дважды. В расширениях это часто случается случайно при попытке «перехватить» стандартное поведение кнопки.
Если пересоздание команды не помогло, рассмотрим дополнительные методы диагностики:
1. Замер производительности
Включите «Замер производительности» (Отладка — Замер производительности) перед нажатием на проблемную кнопку. После того как формы откроются, остановите замер. В появившейся таблице вы увидите количество вызовов каждой строки кода. Если число вызовов в колонке «Количество» больше 1 для ваших процедур — ищите дубли в метаданных или событиях.
2. Проверка фокуса и синхронных вызовов
Иногда «прыжки» связаны с модальностью или синхронными вызовами. Хотя ОткрытьФорму является асинхронным методом, в некоторых режимах работы (особенно в веб-клиенте или при блокировке всего интерфейса) платформа может некорректно восстанавливать контекст после открытия окна.
Попробуйте переписать вызов с использованием описания оповещения, чтобы явно разделить моменты открытия и возврата управления:
&НаКлиенте
Процедура Вложения_ПрикрепитьФайл()
ОписаниеОповещения = Новый ОписаниеОповещения("ПослеЗакрытияФормыВложений", ЭтотОбъект);
ПараметрыФормы = Новый Структура("Основание", Объект.Ссылка);
ОткрытьФорму("Справочник.ПрикрепленныеФайлы.Форма.ФормаЭлемента",
ПараметрыФормы,
ЭтотОбъект,
ЭтаФорма.УникальныйИдентификатор,
,,,
ОписаниеОповещения);
КонецПроцедуры
&НаКлиенте
Процедура ПослеЗакрытияФормыВложений(Результат, ДополнительныеПараметры) Экспорт
// Код, который должен выполниться после закрытия формы
КонецПроцедуры
Подведем итог. Если ваша 1С «сама» переходит на начало процедуры или дублирует действия, это не магия, а следствие конфликта в метаданных расширения или логике событий. Для решения проблемы придерживайтесь следующих правил:
Используя эти методы, мы сможем создавать стабильные расширения, которые работают предсказуемо и не вызывают путаницы у пользователей и разработчиков.