Как устранить зависание 1С 7.7 при совместной работе FormEx, Йокселя и DialMail

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

При разработке механизмов автоматической рассылки счетов или отчетов из 1С:Предприятия 7.7 программисты часто сталкиваются с проблемой «зависания» системы. Типичный сценарий: используется связка из FormEx (для расширения возможностей интерфейса), Йоксель (для генерации PDF) и DialMail (для отправки почты). При обработке небольшого количества документов (до 10 штук) все проходит гладко, но при попытке отправить пакет из 50–100 документов интерфейс 1С замирает, на экране скапливаются открытые формы, и приложение перестает отвечать. В этой статье мы подробно разберем, почему это происходит и как правильно организовать пакетную обработку без визуальных «артефактов» и утечек памяти.

Проанализируем причину зависания: очередь сообщений Windows

Первым делом разберем, почему стандартный подход с методом ОткрытьФорму() и последующим Закрыть() в цикле приводит к коллапсу. Платформа 1С 7.7 является однопоточным приложением. Когда мы запускаем цикл по документам и внутри вызываем открытие формы, система тратит значительные ресурсы на отрисовку графических объектов (GDI). Команда Форма.Закрыть() не уничтожает окно мгновенно — она лишь отправляет сообщение в очередь Windows (Window Messages), уведомляющее систему о том, что окно нужно закрыть.

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

Решение №1: Использование метода «невидимого» выполнения через СтатусВозврата(0)

Самый эффективный способ избежать зависания интерфейса — это вообще не создавать окно формы. Мы можем передать в форму специальный параметр, выполнить всю необходимую работу (генерацию PDF через Йоксель и отправку через DialMail) еще до того, как форма будет отрисована, и сразу отменить открытие. Разберем этот алгоритм по шагам.

В вызывающей обработке (групповой рассылке) мы заменим прямой вызов процедур FormEx на передачу СпискаЗначений в качестве параметра формы:


// В обработке групповой печати/отправки
СЗ = СоздатьОбъект("СписокЗначений");
СЗ.Установить("ВыполнитьПечать", 1);
СЗ.Установить("АдресПочты", Контрагент.ЭлПочта);

// Открываем форму, передавая список параметров
ОткрытьФорму(Док.ТекущийДокумент(), СЗ);

// После отработки формы можем проверить результат, если форма его вернула в список
РезультатПечати = СЗ.Получить("Результат");
Если РезультатПечати <> 1 Тогда
    Сообщить("Ошибка обработки документа " + Док.НомерДок);
КонецЕсли;

Теперь проанализируем изменения, которые необходимо внести в модуль формы самого документа (например, Счета). Основная логика переносится в предопределенную процедуру ПриОткрытии():


Процедура ПриОткрытии()
    _Параметр = Форма.Параметр;
    
    // Проверяем, открыта ли форма программно для печати
    Если ТипЗначенияСтр(_Параметр) = "СписокЗначений" Тогда
        Если _Параметр.Получить("ВыполнитьПечать") = 1 Тогда
            
            // Вызываем вашу функцию формирования PDF и отправки почты
            // Например, ПечатьВPDF_и_Отправить(_Параметр.Получить("АдресПочты"));
            РезультатОбработки = ПроцедураПечатиИОтправки();
            
            // Возвращаем результат обратно в вызывающую обработку
            _Параметр.Установить("Результат", РезультатОбработки);
            
            // ГЛАВНЫЙ МОМЕНТ: СтатусВозврата(0) предотвращает визуальное открытие формы
            СтатусВозврата(0);
            Возврат;
        КонецЕсли;
    КонецЕсли;
    
    // Обычная логика открытия для пользователя
    ...
КонецПроцедуры

При таком подходе дескриптор окна (HWND) не создается, графические ресурсы не расходуются, и 1С может обрабатывать тысячи документов без деградации производительности интерфейса.

Решение №2: Проблемы многопоточности в DialMail

Компонента DialMail (особенно версии 2.7.x) имеет свои особенности работы с потоками. Рассмотрим ситуацию, когда параметр метода ОтправитьПисьмо влияет на стабильность цикла. В некоторых версиях компоненты попытка отправить письмо во втором (фоновом) потоке внутри быстрого цикла 1С приводит к тому, что новые потоки создаются быстрее, чем закрываются старые.

Выясним причину: если SMTP-сервер отвечает с задержкой, а мы вызываем Почта.ОтправитьПисьмо(..., 1), управление сразу возвращается в 1С, и цикл переходит к следующему документу. В итоге одновременно может быть запущено несколько десятков сетевых соединений. Рекомендуется использовать синхронный режим отправки (в основном потоке), чтобы 1С дождалась фактического завершения передачи письма перед началом обработки следующего счета.

Посмотрим на правильный вызов метода отправки:


// Режим отправки: 0 - дождаться завершения (синхронно), 1 - в фоне (асинхронно)
// Для массовой рассылки лучше использовать 0 или явно не указывать фоновый режим
РезультатОтправки = Отправка.ОтправитьПисьмо(0, 0); 
// Где первый параметр - флаг необходимости диалога, второй - поток

Если вы все же сталкиваетесь с «белым экраном» при длительной работе, попробуйте добавить небольшую паузу в цикле, используя компоненту FormEx, чтобы дать системе время на очистку внутренних буферов:


Сервис = СоздатьОбъект("Сервис");
// Пауза в 500 миллисекунд между документами
Сервис.ВнешнееСобытие("Пауза", "500", ""); 

Оптимизация работы с Йокселем и освобождение ресурсов

При использовании Йокселя для конвертации в PDF важно помнить о ручном управлении объектами. Если внутри цикла вы создаете объекты типа ТабличныйДокумент или Конвертер, они могут удерживать память до тех пор, пока переменная не будет переопределена или не выйдет из области видимости. В условиях 1С 7.7 это критично.

Рассмотрим рекомендации по работе с объектами Йокселя:

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

Золотое правило загрузки компонент

Многие проблемы совместимости FormEx, Йокселя и DialMail решаются соблюдением правильной последовательности их инициализации в процедуре ПриНачалеРаботыСистемы. Существует эмпирически установленный порядок, минимизирующий конфликты перехвата системных событий:

  1. FormEx.dll — загружается первой, так как она наиболее глубоко интегрируется в ядро 1С.
  2. SpreadSheet.dll (Йоксель) — вторая в очереди.
  3. DialMail.dll — загружается в последнюю очередь.

Проанализируем ваш код загрузки. Если компоненты загружаются непосредственно перед использованием внутри обработки, это может вызвать нестабильность. Рекомендуется один раз загрузить их при старте системы и обращаться к ним через СоздатьОбъект.

Итоги и рекомендации

Подводя итог, можно сказать, что стабильная работа 1С 7.7 с внешними компонентами при больших нагрузках требует отказа от визуальных операций. Переход на модель работы через СтатусВозврата(0) в процедуре ПриОткрытии() не только убирает проблему зависания, но и значительно ускоряет работу обработки, так как системе не нужно тратить время на отрисовку окон. Совмещая этот метод с правильным порядком загрузки библиотек и контролем сетевых потоков в DialMail, вы получите надежный инструмент для автоматизации бизнес-процессов.

← На главную