Разработчики, работающие с платформой 1С:Предприятие 7.7, часто сталкиваются со специфическим поведением оконного интерфейса. По умолчанию любое обычное (немодальное) окно обработки или документа стремится развернуться на всю рабочую область MDI-контейнера (Multi-Document Interface). Чтобы этого избежать, программисты часто прибегают к вызову метода ОткрытьФормуМодально(), так как модальные окна сохраняют свои исходные геометрические размеры и не растягиваются.
Однако модальный режим накладывает жесткие ограничения на интерфейс. Если из такой формы попытаться вызвать другую обработку, например, стандартную обработку «КомментарийВыполняемыхДействий» для вывода логов или ошибок, ее табличная часть просто не будет отображаться. В этой статье мы подробно разберем, почему так происходит, проанализируем физику процесса на уровне операционной системы Windows и рассмотрим несколько способов решения этой задачи — от экстремального хака ядра 1С до безопасных архитектурных альтернатив.
Выясним причину, почему ломается отрисовка подчиненных окон при вызове из модального режима. В основе интерфейса 1С 7.7 лежит стандартная многодокументная архитектура Windows (MDI). Когда мы открываем форму модально:
EnableWindow(hWndMDIClient, FALSE).ОткрытьФорму(), платформа создаст ее как дочерний элемент заблокированного MDI-контейнера.WM_PAINT. Как результат — форма открывается, но ее табличные части и другие элементы интерфейса остаются «невидимыми», белыми или зависшими.Посмотрим, как можно решить эту проблему различными путями.
Рассмотрим детально оригинальное техническое решение, к которому пришел автор темы. Этот способ основан на низкоуровневом обращении к операционной системе через внешнюю компоненту DynamicWrapperX (или аналогичный инструмент работы с WinAPI) и прямому вызову недокументированной функции ядра платформы 1С 7.7 из библиотеки seven.dll.
Для реализации этого метода в модуле формы, которая открывается модально, в процедуре ПослеОткрытия() необходимо вызвать специальный код отключения модального режима на уровне операционной системы.
Разберем по шагам программный код, реализующий этот хак:
Процедура ОтключитьМодальность(ФормаКонтекст = "")
Если ПустоеЗначение(ФормаКонтекст) = 1 Тогда
Сервис = СоздатьОбъект("Сервис");
ТекущийКонтекст = "";
Сервис.АктивныйКонтекст(ТекущийКонтекст);
Если ТипЗначенияСтр(ТекущийКонтекст) = "ГрупповойКонтекст" Тогда
ФормаКонтекст = ТекущийКонтекст.Форма;
КонецЕсли;
КонецЕсли;
Если ПустоеЗначение(ФормаКонтекст) = 0 Тогда
// Используем FormEx для получения системного хэндла (HWND) окна формы
РасширениеФормы = СоздатьОбъект("РасширениеФормы");
РасширениеФормы.УстановитьФорму(ФормаКонтекст);
Хэндл = РасширениеФормы.Дескриптор;
// Подключаем DynamicWrapperX для работы с WinAPI
DWX = СоздатьОбъект("DynamicWrapperX");
DWX.Register("User32", "EnableWindow", "i=hl", "r=l");
DWX.Register("User32", "GetForegroundWindow","r=h");
DWX.Register("User32", "SetForegroundWindow", "i=h", "r=l");
DWX.Register("User32", "GetParent", "i=h", "r=h");
// Регистрируем недокументированную C++ функцию из ядра 1С 7.7 (seven.dll)
// Сигнатура функции после деманглинга: int __cdecl SetModalMode(int)
DWX.Register("Seven.dll:?SetModalMode@@YAHH@Z", "SetModalMode", "i=l", "r=l");
// Находим родительское окно MDI Client
Окно1C = DWX.GetParent(Хэндл);
Если ПустоеЗначение(Окно1C) = 0 Тогда
// Принудительно разблокируем родительское окно для ОС Windows
DWX.EnableWindow(Окно1C, 1);
DWX.SetForegroundWindow(Окно1C);
КонецЕсли;
// Сбрасываем внутренний флаг модальности в самом ядре 1С
DWX.SetModalMode(0);
КонецЕсли;
КонецПроцедуры
Проанализируем ситуацию и принцип работы данного кода. Функция ?SetModalMode@@YAHH@Z управляет внутренним триггером ядра 1С, который указывает системе, выполняется ли сейчас модальный диалог. Вызывая SetModalMode(0), мы сообщаем платформе, что модальный режим завершен. Платформа тут же разблокирует MDI Client. При этом стек выполнения встроенного языка 1С не прерывается — код модальной формы продолжает работать в обычном режиме, но теперь из нее можно штатно открывать любые дочерние окна, включая КомментарийВыполняемыхДействий, и их таблицы будут прорисовываться корректно.
Важное предупреждение: Использование прямого вмешательства в память и функции seven.dll несет серьезные риски стабильности системы. Это может вызывать критические ошибки доступа к памяти (Access Violation), приводить к аварийному завершению работы программы (APPCRASH) при закрытии окон, особенно в многопользовательском терминальном режиме (RDP) под управлением современных серверных ОС.
Если мы хотим избежать потенциально опасных игр с ядром платформы, проанализируем более безопасную альтернативу. Мы можем полностью отказаться от метода ОткрытьФормуМодально(), открывать форму как обычную немодальную, но программно заставить ее вести себя как модальную.
Для этого нам понадобится популярная внешняя компонента FormEx. Разберем этот алгоритм по шагам:
FormEx.Посмотрим на пример реализации данного подхода. В модуле открываемой формы напишем следующий код:
Процедура ПриОткрытии()
// Создаем объект расширения формы из библиотеки FormEx
EF = СоздатьОбъект("РасширениеФормы");
EF.УстановитьФорму(Форма);
// Устанавливаем фиксированный стиль окна (запрещаем максимизацию)
// Стиль 2 убирает стандартные кнопки развертывания окна
EF.СтильОкна = 2;
// Блокируем родительский контейнер, эмулируя модальный режим
Сервис = СоздатьОбъект("Сервис");
Сервис.БлокироватьОкноВладельца(Форма, 1);
КонецПроцедуры
Процедура ПриЗакрытии()
// Обязательно снимаем блокировку с родительского окна при закрытии формы
Сервис = СоздатьОбъект("Сервис");
Сервис.БлокироватьОкноВладельца(Форма, 0);
КонецПроцедуры
Этот способ абсолютно безопасен, так как использует стандартные высокоуровневые методы расширения возможностей платформы, не ломая логику работы системных потоков и сообщений Windows. Любые дочерние немодальные окна будут открываться и прорисовываться без сбоев.
Если специфика бизнес-логики требует использования именно честного модального окна 1С, но стандартные механизмы вывода логов не работают, мы можем изменить сам способ визуализации данных. Вместо вызова внешней обработки с таблицей («КомментарийВыполняемыхДействий») мы можем встроить функционал отображения информации прямо внутрь нашей модальной формы.
Для этого идеально подходит внешняя компонента 1С++ и её класс ТабличноеПоле. Разберем преимущества этого решения:
1С++ создает элемент управления ТабличноеПоле непосредственно привязываясь к физическому дескриптору (HWND) нашей модальной формы.Чтобы вывести лог, достаточно разместить на форме текстовый шаблон-контейнер, инициализировать в нем объект класса ТабличноеПоле и динамически добавлять туда строки в процессе выполнения кода.
Рассмотрим еще один простой архитектурный прием, который не требует использования внешних компонент. Если нам крайне важно вывести отчет обработки «КомментарийВыполняемыхДействий», мы можем изменить последовательность открытия окон:
ОткрытьФорму("Обработка.КомментарийВыполняемыхДействий", КонтекстПередачи).ОткрытьФормуМодально().Мы проанализировали физику конфликта отрисовки окон в 1С 7.7 и рассмотрели четыре различных способа решения проблемы. Подведем итоги: