Как в 1С 7.7 открыть форму без разворачивания на весь экран и обойти блокировку дочерних окон?

Программист 1С v7.7 IT и автоматизация бизнеса
← На главную

Разработчики, работающие с платформой 1С:Предприятие 7.7, часто сталкиваются со специфическим поведением оконного интерфейса. По умолчанию любое обычное (немодальное) окно обработки или документа стремится развернуться на всю рабочую область MDI-контейнера (Multi-Document Interface). Чтобы этого избежать, программисты часто прибегают к вызову метода ОткрытьФормуМодально(), так как модальные окна сохраняют свои исходные геометрические размеры и не растягиваются.

Однако модальный режим накладывает жесткие ограничения на интерфейс. Если из такой формы попытаться вызвать другую обработку, например, стандартную обработку «КомментарийВыполняемыхДействий» для вывода логов или ошибок, ее табличная часть просто не будет отображаться. В этой статье мы подробно разберем, почему так происходит, проанализируем физику процесса на уровне операционной системы Windows и рассмотрим несколько способов решения этой задачи — от экстремального хака ядра 1С до безопасных архитектурных альтернатив.

Суть проблемы: конфликт MDI-интерфейса и модального режима

Выясним причину, почему ломается отрисовка подчиненных окон при вызове из модального режима. В основе интерфейса 1С 7.7 лежит стандартная многодокументная архитектура Windows (MDI). Когда мы открываем форму модально:

  1. Платформа 1С блокирует главное окно MDI Client (область, в которой отображаются все журналы, документы и отчеты), посылая ему системную команду отключения EnableWindow(hWndMDIClient, FALSE).
  2. Запускается локальный цикл обработки сообщений Windows. Этот цикл перехватывает любые события мыши и клавиатуры, перенаправляя их исключительно в активное модальное окно.
  3. Если из этого состояния попытаться открыть немодальную форму через метод ОткрытьФорму(), платформа создаст ее как дочерний элемент заблокированного MDI-контейнера.
  4. Поскольку родительский контейнер заблокирован, новое окно не может получить фокус ввода и не обрабатывает системные сообщения перерисовки, такие как WM_PAINT. Как результат — форма открывается, но ее табличные части и другие элементы интерфейса остаются «невидимыми», белыми или зависшими.

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

Решение 1: Снятие модальности «на лету» через WinAPI и Seven.dll

Рассмотрим детально оригинальное техническое решение, к которому пришел автор темы. Этот способ основан на низкоуровневом обращении к операционной системе через внешнюю компоненту 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) под управлением современных серверных ОС.

Решение 2: Безопасная эмуляция модальности с помощью FormEx

Если мы хотим избежать потенциально опасных игр с ядром платформы, проанализируем более безопасную альтернативу. Мы можем полностью отказаться от метода ОткрытьФормуМодально(), открывать форму как обычную немодальную, но программно заставить ее вести себя как модальную.

Для этого нам понадобится популярная внешняя компонента FormEx. Разберем этот алгоритм по шагам:

  1. При открытии формы отключаем у нее системные кнопки изменения размера и разворачивания, чтобы она не растягивалась на весь экран. Это делается через установку стиля окна в компоненте FormEx.
  2. Блокируем фокус на родительском окне, имитируя классическое модальное поведение. Пользователь не сможет кликнуть на другие элементы интерфейса 1С, пока открыта наша форма.

Посмотрим на пример реализации данного подхода. В модуле открываемой формы напишем следующий код:


Процедура ПриОткрытии()
    // Создаем объект расширения формы из библиотеки FormEx
    EF = СоздатьОбъект("РасширениеФормы");
    EF.УстановитьФорму(Форма);
    
    // Устанавливаем фиксированный стиль окна (запрещаем максимизацию)
    // Стиль 2 убирает стандартные кнопки развертывания окна
    EF.СтильОкна = 2; 
    
    // Блокируем родительский контейнер, эмулируя модальный режим
    Сервис = СоздатьОбъект("Сервис");
    Сервис.БлокироватьОкноВладельца(Форма, 1);
КонецПроцедуры

Процедура ПриЗакрытии()
    // Обязательно снимаем блокировку с родительского окна при закрытии формы
    Сервис = СоздатьОбъект("Сервис");
    Сервис.БлокироватьОкноВладельца(Форма, 0);
КонецПроцедуры

Этот способ абсолютно безопасен, так как использует стандартные высокоуровневые методы расширения возможностей платформы, не ломая логику работы системных потоков и сообщений Windows. Любые дочерние немодальные окна будут открываться и прорисовываться без сбоев.

Решение 3: Использование классов 1С++ и элемента «ТабличноеПоле»

Если специфика бизнес-логики требует использования именно честного модального окна 1С, но стандартные механизмы вывода логов не работают, мы можем изменить сам способ визуализации данных. Вместо вызова внешней обработки с таблицей («КомментарийВыполняемыхДействий») мы можем встроить функционал отображения информации прямо внутрь нашей модальной формы.

Для этого идеально подходит внешняя компонента 1С++ и её класс ТабличноеПоле. Разберем преимущества этого решения:

Чтобы вывести лог, достаточно разместить на форме текстовый шаблон-контейнер, инициализировать в нем объект класса ТабличноеПоле и динамически добавлять туда строки в процессе выполнения кода.

Решение 4: Программный обход через вызов формы до модального режима

Рассмотрим еще один простой архитектурный прием, который не требует использования внешних компонент. Если нам крайне важно вывести отчет обработки «КомментарийВыполняемыхДействий», мы можем изменить последовательность открытия окон:

  1. Перед вызовом модального окна мы сначала открываем немодальную форму вывода комментариев с помощью стандартного метода ОткрытьФорму("Обработка.КомментарийВыполняемыхДействий", КонтекстПередачи).
  2. В этот момент MDI-контейнер еще свободен, форма инициализируется, создает свои элементы управления и полностью прорисовывает пустую таблицу на экране.
  3. Сразу же следующим шагом мы открываем нашу основную форму методом ОткрытьФормуМодально().
  4. В процессе выполнения алгоритмов внутри модального окна мы передаем данные в уже созданную и отрисованную ранее форму комментариев. Поскольку ее графические объекты Windows уже созданы, обновление информации в таблице будет происходить успешно, несмотря на текущую блокировку интерфейса.

Резюме и рекомендации

Мы проанализировали физику конфликта отрисовки окон в 1С 7.7 и рассмотрели четыре различных способа решения проблемы. Подведем итоги:

← На главную