При автоматизации рутинных задач в 1С:Предприятии разработчики часто прибегают к запуску системы из командной строки (через CMD или BAT-файлы) с автоматическим выполнением внешней обработки с помощью параметра /Execute. Однако в процессе работы этой схемы на обычных формах часто возникает неприятная техническая проблема: если во время выполнения алгоритма пользователь переключается на другое приложение, а затем пытается вернуться обратно, окно 1С безнадежно «белеет», перестает обновляться, а в заголовке окна появляется системная надпись операционной системы Windows «Не отвечает». При этом сама обработка в фоновом режиме продолжает выполнять все необходимые действия.
В этой статье мы подробно проанализируем, почему операционная система считает процесс 1С зависшим, разберем физику этого процесса «под капотом» и рассмотрим несколько эффективных практических способов решения данной проблемы.
Чтобы понять природу этого поведения, углубимся в особенности работы графической подсистемы Windows и архитектуры клиентского приложения 1С. Любое стандартное графическое окно в ОС Windows функционирует на основе бесконечного цикла обработки системных сообщений (Message Loop). Операционная система постоянно шлет окну сообщения различных типов:
WM_PAINT — требование перерисовать рабочую область окна (например, когда поверх него протащили другое окно или развернули его из панели задач).WM_ACTIVATE — уведомление о том, что окно получило или потеряло фокус ввода.WM_SIZE — сигнал об изменении размеров окна.Клиентское приложение 1С:Предприятие работает в однопоточном режиме обработки интерфейса (так называемый UI-тред). Когда мы запускаем тяжелый, длительный цикл вычислений непосредственно в основном потоке, этот поток оказывается полностью занят расчетами. Он физически не успевает обрабатывать входящую очередь сообщений от операционной системы Windows.
Но почему при ручном запуске обработки через меню Файл — Открыть все работает корректно, а при запуске из командной строки окно зависает? Разгадка кроется в моменте старта алгоритма:
/Execute код обработки часто начинает выполняться **сразу же** — внутри предопределенных событий ПриОткрытии или ПередОткрытием. В этот критический момент окно формы еще не успело полностью завершить процедуру инициализации, отрисовки и регистрации в диспетчере окон Windows. Поток сразу же уходит в плотные вычисления. Как только фокус теряется, Windows шлет сигналы деактивации, а при попытке вернуться — сигналы перерисовки. Не получая ответа от занятого потока 1С в течение нескольких секунд, операционная система считает процесс «мертвым» и заменяет его статичным системным «призраком» с надписью «Не отвечает».Рассмотрим по шагам практические методы решения этой проблемы, начиная с самых простых и заканчивая наиболее современными архитектурными подходами.
Это самый простой, красивый и надежный способ реанимировать интерфейс при автозапуске. Наша задача — не дать тяжелому алгоритму запуститься в момент открытия формы. Нам нужно позволить форме полностью открыться, отобразиться на экране, «подружиться» с операционной системой и только потом начать работу.
Для этого мы воспользуемся встроенным методом ПодключитьОбработчикОжидания(). Давайте посмотрим, как правильно переписать код инициализации внешней обработки:
Процедура ПриОткрытии()
// Вместо непосредственного вызова тяжелой процедуры,
// даем форме 0.1 секунды на завершение отрисовки в ОС
ПодключитьОбработчикОжидания("ВыполнитьДлинныйЦиклОтложено", 0.1, Истина);
КонецПроцедуры
Процедура ВыполнитьДлинныйЦиклОтложено() Экспорт
// Снимаем обработчик, чтобы он не выполнился повторно
ОтключитьОбработчикОжидания("ВыполнитьДлинныйЦиклОтложено");
// Запускаем наш основной алгоритм
ВыполнитьОсновнойРасчет();
КонецПроцедуры
Почему это помогает: Метод ПодключитьОбработчикОжидания с третьим параметром Истина (однократное выполнение) сработает только тогда, когда платформа 1С перейдет в состояние обработки ожидания (Idle State). К этому моменту графическая оболочка уже полностью построена, зарегистрирована в Windows и готова адекватно реагировать на переключение фокуса между окнами.
ОбработкаПрерыванияПользователя()Если наш цикл вычислений внутри процедуры ВыполнитьОсновнойРасчет() длится очень долго (минуты или часы), даже отложенный старт не спасет от зависания при переключении окон, поскольку поток все равно надолго заблокируется. Нам необходимо периодически «отдавать» управление операционной системе, чтобы она могла обработать накопившиеся в очереди графические сообщения.
Для этого идеально подходит стандартный метод 1С ОбработкаПрерыванияПользователя(). Мало кто знает, но помимо своей основной задачи (отслеживание нажатия комбинации клавиш Ctrl + Break для аварийного останова), этот метод принудительно инициирует прокачку очереди сообщений операционной системы Windows.
Давайте проанализируем пример правильного внедрения этого метода в тело длительного цикла:
Процедура ВыполнитьОсновнойРасчет()
Выборка = Справочники.Номенклатура.Выбрать();
Итератор = 0;
Пока Выборка.Следующий() Цикл
Итератор = Итератор + 1;
// Чтобы не перегружать процессор частыми вызовами системных прерываний,
// вызываем метод, например, каждую сотую или тысячную итерацию
Если Итератор % 100 = 0 Тогда
ОбработкаПрерыванияПользователя();
КонецЕсли;
// Имитация полезной работы
ОбработатьЭлементНоменклатуры(Выборка.Ссылка);
Сообщить("Обработано элементов: " + Итератор);
КонецЦикла;
КонецПроцедуры
Важное примечание: Если итерации вашего цикла проходят очень быстро (микросекунды), вызов ОбработкаПрерыванияПользователя() на каждом шаге может замедлить общее время выполнения алгоритма. Использование оператора остатка от деления % (например, выполнять раз в 100 или 500 итераций) позволяет соблюсти идеальный баланс между отзывчивостью интерфейса и производительностью расчетов.
Если в процессе выполнения алгоритма вы выводите текстовую информацию на саму форму (например, изменяете текст надписи состояния или двигаете ползунок шкалы Индикатор), платформа может не успевать визуально перерисовывать эти элементы на экране.
Для принудительного обновления графического состояния элементов интерфейса следует использовать специализированные методы платформы:
ЭтаФорма.Обновить().ОбновитьОтображение().Посмотрим на практическую реализацию визуального прогресс-бара:
Процедура ВыполнитьДлинныйРасчетСВыводомНаФорму()
ВсегоДокументов = ТаблицаДокументов.Количество();
ЭлементыФормы.ИндикаторПрогресса.МаксимальноеЗначение = ВсегоДокументов;
Для Индекс = 0 По ВсегоДокументов - 1 Цикл
// Выполняем расчет
ОбработатьСтрокуТаблицы(ТаблицаДокументов[Индекс]);
// Обновляем состояние индикатора на форме
ЭлементыФормы.ИндикаторПрогресса.Значение = Индекс + 1;
ЭлементыФормы.НадписьСтатуса.Заголовок = "Обработано: " + (Индекс + 1) + " из " + ВсегоДокументов;
// Принудительно заставляем Windows перерисовать форму прямо сейчас
ЭтаФорма.Обновить();
// Не забываем про прокачку сообщений ОС
ОбработкаПрерыванияПользователя();
КонецЦикла;
КонецПроцедуры
Если ваша конфигурация работает на платформе 8.3, наиболее правильным архитектурным решением будет полный отказ от выполнения тяжелых расчетов непосредственно в интерфейсном потоке клиента. Для этого мы можем задействовать механизм фоновых заданий.
При таком подходе схема работы выглядит следующим образом:
ФоновыеЗадания.Разберем ситуацию с точки зрения здравого смысла. Если ваш cmd-файл запускается по расписанию (например, ночью на сервере через планировщик Windows Task Scheduler), то выводить сообщения на экран компьютера в принципе не имеет смысла — их никто не увидит. Более того, любая попытка отрисовки интерфейса в сессии планировщика задач (особенно если запуск происходит без входа пользователя в систему, в режиме "Run whether user is logged on or not") может приводить к непредсказуемым ошибкам графической подсистемы Windows.
В этом случае лучшим решением будет полный отказ от вывода окон на экран. Для этого используем параметры запуска командной строки 1С:
/Out "C:\Logs\log.txt" -NoKeep перенаправит все сообщения, отправленные через метод Сообщить(), в текстовый файл лога. При этом само окно сообщений на клиенте инициализироваться не будет, что сэкономит системные ресурсы и полностью устранит проблему с зависанием графики.ЗавершитьРаботуСистемы(Ложь) в коде 1С, чтобы сеанс корректно закрывался, не оставляя «висящих» процессов в диспетчере задач.Мы подробно проанализировали проблему потери фокуса и зависания окон 1С при работе из командной строки. Подводя итог, дадим краткую рекомендацию разработчикам: всегда разделяйте графическую инициализацию формы и тяжелые вычислительные алгоритмы. Использование простой задержки через ПодключитьОбработчикОжидания() в сочетании с регулярным вызовом ОбработкаПрерыванияПользователя() позволит вашим внешним обработкам работать стабильно, предсказуемо и профессионально в любых режимах запуска.