Разработчики, работающие с Обычным приложением на современных версиях платформы 1С:Предприятие 8.3 (начиная с релизов 8.3.26.1303 и 8.3.27.1326), все чаще сталкиваются с критической проблемой: при попытке программного закрытия формы документа или обработки приложение «зависает намертво». При этом стандартные механизмы перехвата исключений не срабатывают, а отладка прерывается на строке выполнения метода Закрыть(). В этой статье мы подробно разберем, почему это происходит, и рассмотрим проверенные способы обхода данной ошибки.
Проанализируем ситуацию: на версии платформы 8.3.25 код работает корректно, а на последующих обновлениях возникают сбои. Это явный признак регрессии — ошибки, возникшей в результате изменений в самом движке 1С. Основная причина кроется в глубокой модернизации графического стека и механизмов работы с памятью, которые в новых версиях ориентированы на Управляемые формы и мобильную платформу.
Рассмотрим основные факторы, провоцирующие «deadlock» (взаимную блокировку) интерфейса:
Закрыть() платформа инициирует цепочку событий ПередЗакрытием и ПриЗакрытии. В новых версиях ядра возникает ситуация, когда фокус ввода или системные сообщения Windows не могут быть корректно переданы между родительским и дочерним окнами.Выясним причину, почему код автора из примера может работать нестабильно. Рассмотрим программный фрагмент:
Форма = Ссылка.ПолучитьФорму("ФормаДокумента");
Если Форма.Открыта() Тогда
Форма.Закрыть();
КонецЕсли;
Проблема заключается в том, что метод ПолучитьФорму() без указания ключа уникальности или владельца в новых версиях платформы может не найти уже существующее на экране окно, а создать новый программный объект, связанный с той же ссылкой. Попытка закрыть такой объект, когда основное окно еще активно, вызывает конфликт дескрипторов HWND.
Вместо этого мы рекомендуем использовать поиск по открытым окнам клиентского приложения. Разберем правильный подход:
// Ищем форму среди уже открытых окон, чтобы не создавать дубликат
Окна = ПолучитьОкнаКлиентскогоПриложения();
Для Каждого Окно Из Окна Цикл
Контент = Окно.Содержимое;
Если ТипЗнч(Контент) = Тип("Форма") И Контент.ПараметрСсылка = Ссылка Тогда
// Нашли именно ту форму, которая открыта пользователем
Контент.Закрыть();
Прервать;
КонецЦикла;
Такой метод гарантирует, что мы взаимодействуем с реальным графическим объектом, который видит пользователь, что значительно снижает риск зависания интерфейса.
Зачастую зависание происходит из-за того, что команда Закрыть() вызывается в тот момент, когда платформа еще занята обработкой другого события (например, нажатия кнопки или изменения поля). Мы можем применить «трюк» с асинхронным вызовом, чтобы позволить платформе завершить текущие операции перед закрытием окна.
Рассмотрим, как это реализовать. В модуле формы или обработки создадим процедуру для закрытия, а вызывать её будем через ПодключитьОбработчикОжидания.
Шаг 1. В месте, где нужно закрыть форму, пишем:
// Устанавливаем минимальную задержку в 0.1 секунды
ПодключитьОбработчикОжидания("ВыполнитьЗакрытиеФормыПрограммно", 0.1, Истина);
Шаг 2. Описываем саму процедуру:
Процедура ВыполнитьЗакрытиеФормыПрограммно() Экспорт
ЭтаФорма.Закрыть();
КонецПроцедуры
Этот подход позволяет полностью очистить программный стек и избежать ситуации, когда один и тот же поток пытается одновременно отрисовать изменения в форме и уничтожить её объект.
Если ваша конфигурация «сильно переделана», как указано в исходном вопросе, проверьте наличие в формах устаревших ActiveX элементов или специфических настроек оформления. Проанализируем ситуацию с режимом совместимости. Даже если вы устанавливаете значение «Версия 8.3.25», физический исполняемый файл платформы 8.3.27 использует новые библиотеки отрисовки (GDI+/DirectX).
Рекомендации по диагностике:
ПолеHTMLДокумента. Если зависания прекратились — проблема в обновленном движке WebKit/Chromium, который 1С использует внутри. В этом случае поможет только перенос логики HTML во внешнее окно или использование упрощенного форматированного текста.Закрыть() провоцируется глобальным модулем (для поиска ошибок в таких модулях удобно использовать специализированную конфигурацию для анализа), который пытается выполнить запись в лог или проверку прав в момент уничтожения объекта формы.Выполнить("Форма.Закрыть();"). Проверить проект на наличие таких конструкций поможет статический анализатор кода.Подведем итоги нашего исследования. Если вы столкнулись с «мертвым» зависанием обычных форм на новых релизах 1С 8.3:
ПолучитьФорму(). Перейдите на поиск окон через ПолучитьОкнаКлиентскогоПриложения().ПодключитьОбработчикОжидания для выноса команды закрытия из текущего потока выполнения.Помните, что развитие платформы 1С сейчас сосредоточено на управляемом интерфейсе, поэтому для старых решений на обычных формах использование самых свежих релизов платформы всегда сопряжено с риском встретить подобные ошибки ядра.