При автоматизации складского учета, логистики или массовой выписки документов в среде 1С:Предприятие 7.7 разработчики часто сталкиваются с неприятным визуальным эффектом. Когда система в цикле отправляет на принтер десятки или сотни документов с помощью метода Таб.Напечатать(0), экран компьютера начинает хаотично мигать. Постоянно появляющееся и исчезающее маленькое окно «Печать документа...» забирает и возвращает фокус ввода, из-за чего работать в других программах (или даже в самой 1С) во время этого процесса становится невозможно.
В этой статье мы подробно разберем, почему возникает этот эффект с точки зрения операционной системы Windows и библиотеки MFC, а также рассмотрим несколько практических способов полного подавления этого мелькания.
Платформа 1С 7.7 построена с использованием классической библиотеки Microsoft Foundation Classes (MFC). В частности, за вывод диалоговых окон и взаимодействие с принтером отвечает системная библиотека mfc42.dll.
Когда в коде 1С мы вызываем метод Таб.Напечатать(0), управление передается стандартной функции MFC CView::OnFilePrint(). Проанализируем последовательность действий, которую выполняет эта функция на системном уровне:
AfxGetMainWnd()->EnableWindow(FALSE). Это деактивирует главное окно 1С, делая его недоступным для кликов пользователя на время выполнения операции. Именно в этот момент окно 1С визуально «блекнет», сигнализируя о потере активности.CPrintingDialog). Программа принудительно вызывает метод ShowWindow(SW_SHOW) (где параметр SW_SHOW равен 05 в шестнадцатеричном формате). Окно появляется на экране поверх всех остальных и забирает фокус.AfxGetMainWnd()->EnableWindow(TRUE), возвращающая фокус и активность главному окну 1С.Если мы отправляем пачку из 50 документов, эта цепочка блокировок, активаций и отрисовок окон повторяется 50 раз подряд за очень короткий промежуток времени. Операционная система не успевает корректно обрабатывать такие быстрые изменения интерфейса, что приводит к дикому мерцанию экрана и зависанию графической оболочки Windows.
Для решения этой проблемы нам потребуется заставить Windows «думать», что окно создается, но физически его не отрисовывать, а также отключить принудительную блокировку главного окна 1С. Рассмотрим варианты реализации.
---
Самый надежный способ убрать окно прогресса печати — изменить параметры вызова функции ShowWindow в библиотеке mfc42.dll. Нам нужно заменить инструкцию передачи параметра SW_SHOW (значение 05) на SW_HIDE (значение 00).
Модифицировать системный файл mfc42.dll в папке C:\Windows\System32 напрямую крайне опасно, поскольку эта библиотека используется многими старыми программами в операционной системе. Вместо этого мы применим штатный механизм Windows — DLL Redirection.
Выполним следующие шаги:
C:\Program Files (x86)\1Cv77\BIN\).1cv7s.exe или 1cv7.exe)..local. Например: 1cv7s.exe.local.mfc42.dll из системной папки C:\Windows\System32\ во вновь созданную папку 1cv7s.exe.local. Теперь при запуске 1С операционная система будет загружать именно локальную копию библиотеки, игнорируя системную.mfc42.dll в любом HEX-редакторе (например, HxD) и внесем изменения. Нам необходимо найти процедуру CView::OnFilePrint() (ее ординал в библиотеке — 4613).0x516, где располагается вызов CWnd::ShowWindow. Нам необходимо найти ассемблерную инструкцию PUSH 5 (в машинном коде это байты 6A 05) и заменить ее на PUSH 0 (байты 6A 00).После сохранения изменений при вызове метода Таб.Напечатать(0) окно «Печать документа...» будет создаваться в скрытом режиме (флаг SW_HIDE), и пользователь его не увидит.
---
Если мы не хотим вручную модифицировать файлы на диске на каждом рабочем месте, мы можем изменять байты библиотеки прямо в оперативной памяти запущенного процесса 1С. Для этого можно использовать специализированную внешнюю компоненту или скрипт на базе библиотеки DynWrapX.dll.
Рассмотрим пример алгоритма поиска нужного паттерна в памяти и его замены «на лету». Сигнатура, которую мы ищем в теле загруженной библиотеки mfc42.dll для скрытия окна, выглядит как FFFF6A058D. Нам нужно заменить байт 05 на 00.
Ниже представлен пример кода на языке 1С 7.7, демонстрирующий логику, которую выполняет внешняя компонента для поиска адресов функций деактивации главного окна EnableWindow и его обхода:
// Пример логики поиска сигнатур для патча вызовов EnableWindow
EnableWindowAddr = GetProcAddress(hDll, "?EnableWindow@CWnd@@QAEHH@Z"); // Ординал 2642
GetMainWndAddr = GetProcAddress(hDll, "?AfxGetMainWnd@@YGPAVCWnd@@XZ"); // Ординал 6575
// Ищем в памяти код вызова деактивации окна:
// E8 xx xx xx FF 8B C8 E8 yy yy yy FF
// Где первый вызов - получение главного окна, второй - EnableWindow(0)
// При нахождении паттерна заменяем PUSH 0 (деактивация) на обход функции (JMP)
Если НайденаСигнатура() Тогда
// Заменяем инструкцию блокировки на переход (прыжок через вызов)
// Тем самым предотвращаем деактивацию и последующее мигание интерфейса 1С
ЗаписатьБайтыВПамять(АдресПатча, "EB 0C");
КонецЕсли;
Применение готовой внешней компоненты (например, разработанной участниками сообщества ВК «ПрогрессПечати») позволяет решить обе проблемы одной строчкой кода при запуске системы: окно прогресса скрывается, а блокировка главного окна 1С отключается, полностью убирая мерцание.
---
Если политика безопасности компании или технические ограничения не позволяют патчить системные библиотеки DLL (даже локально), мы можем использовать полностью штатный «обходной» путь. Мы сохраним печатную форму в файл и отправим ее на печать в фоновом режиме средствами сторонней консольной утилиты.
Для этого мы воспользуемся популярным конвертером табличных документов Йоксель (или возможностями платформы 1С по сохранению в PDF/HTML) и бесплатной утилитой SumatraPDF, которая умеет печатать документы из командной строки без вывода графического интерфейса.
Разберем этот алгоритм по шагам в коде 1С 7.7:
Процедура ПечатьБезМелькания(ТабДок, ИмяПринтера)
// 1. Формируем уникальное имя временного файла
ПутьКФайлу = КаталогВременныхФайлов() + "print_temp.pdf";
// 2. Конвертируем табличный документ в PDF с помощью Йокселя
Конвертер = СоздатьОбъект("Йоксель.КонвертерPDF");
Конвертер.Записать(ТабДок, ПутьКФайлу);
// 3. Формируем команду для SumatraPDF
// Флаг -print-to-default отправляет на принтер по умолчанию
// Флаг -silent подавляет любые диалоговые окна программы
ПутьКСуматре = "C:\Program Files\SumatraPDF\SumatraPDF.exe";
СтрокаЗапуска = """" + ПутьКСуматре + """ -print-to """ + ИмяПринтера + """ -silent """ + ПутьКФайлу + """";
// 4. Запускаем печать в скрытом режиме приложения
Шелл = СоздатьОбъект("WScript.Shell");
Шелл.Run(СтрокаЗапуска, 0, 1); // 0 - скрыть окно консоли, 1 - ожидать завершения
// 5. Удаляем временный файл
ФС.УдалитьФайл(ПутьКФайлу);
КонецПроцедуры
Преимущества данного метода:
---
Еще одним элегантным решением является использование объекта ПолеТабличногоДокумента из внешней компоненты TableDoc.dll (активно используемой разработчиками в связке с расширением 1С++).
В этой компоненте реализован собственный движок печати, который не обращается к стандартным методам CView::OnFilePrint() библиотеки MFC 1С. Вызов печати объекта TableDoc происходит в фоновом режиме, исключая появление каких-либо окон прогресса и предотвращая мерцание интерфейса 1С.
Посмотрим на пример использования:
// Пример печати файла MXL через компоненту TableDoc
ТабДок = СоздатьОбъект("ТабличныйДокумент");
ТабДок.Открыть(ПутьКФайлуMXL);
// Печатаем напрямую без вызова стандартных диалогов MFC 1С
ТабДок.Напечатать(0);
---
Мы проанализировали проблему мерцания интерфейса 1С 7.7 при массовой печати и рассмотрели несколько вариантов ее решения:
mfc42.dll. Это стандартный и надежный способ изоляции изменений.