При разработке и сопровождении конфигураций на платформе 1С:Предприятие 7.7 часто возникает задача запуска внешних отчетов и печатных форм из различных мест конфигурации: из формы документа, из журнала документов или из групповых обработок. Типичная проблема заключается в том, что печатная форма может ожидать контекст открытой формы документа, а при вызове из журнала получает прямую ссылку на документ или контекст списка журнала. В результате при вызове метода ОткрытьФормуМодально() выполнение модуля отчета завершается аварийно, в окно сообщений платформы выводится текст ошибки времени исполнения, а вызывающий код не получает встроенными средствами уведомления о сбое.
Разберем, почему стандартные механизмы языка 1С 7.7 ведут себя именно так, и рассмотрим способы отлова сбоев, подавления системных сообщений, а также архитектурно правильные подходы к решению проблемы.
Первое, что часто пытаются сделать разработчики — это обернуть вызов модального открытия формы в конструкцию обработки исключений:
Попытка
ОткрытьФормуМодально("Отчет", Параметры, ИмяФайла);
Исключение
Предупреждение("Отчет завершился с ошибкой!");
КонецПопытки;
Проанализируем ситуацию: в платформе 1С 7.7 такая конструкция не перехватывает сбои времени выполнения, происходящие внутри открываемого отчета (например, в предопределенной процедуре ПриОткрытии() или в исполняемом теле модуля). Платформа перехватывает ошибку собственным глобальным обработчиком: прерывает инициализацию формы, выводит красное сообщение в окно сообщений и возвращает управление вызывающему коду на следующую строку после вызова ОткрытьФормуМодально(). Блок Исключение при этом не срабатывает, а переданный параметр не успевает модифицироваться отчетом.
Если требуется перехватить ошибку и полностью скрыть вывод системного сообщения во встроенное табло без изменения самого файла отчета, наиболее эффективным инструментом выступает внешняя компонента 1С++. В ней реализован класс Перехватчик, позволяющий контролировать глобальные события платформы.
Разберем реализацию этого механизма по шагам.
УправлениеНастройками: перехват событий глобального контекста и ускорение TurboBL.Событие_НеобработаннаяОшибка. Чтобы подавить вывод штатного сообщения об ошибке в окно платформы, функция должна возвращать значение 0. Если вернуть 1, сообщение выведется в обычном режиме.Посмотрим на пример кода в глобальном модуле:
Перем глТрассировкаОшибок Экспорт;
Перем глСписокНеобработанныхОшибок Экспорт;
Процедура ПриНачалеРаботыСистемы()
ЗагрузитьВнешнююКомпоненту("1cpp.dll");
// Включаем перехват событий глобального контекста
оНастр = СоздатьОбъект("УправлениеНастройками");
оНастр.Включить("TurboBL");
оНастр.Включить("ПерехватитьСобытияГК");
// Инициализируем перехватчик
Перехватчик = СоздатьОбъект("Перехватчик");
Перехватчик.ПодключитьСобытие_НеобработаннаяОшибка();
глСписокНеобработанныхОшибок = СоздатьОбъект("СписокЗначений");
глТрассировкаОшибок = 0;
КонецПроцедуры
Функция Событие_НеобработаннаяОшибка(СообщениеОбОшибке, Маркер, ИмяФайла, Парам4, НомерСтрокиОшибки, Парам6)
Если глТрассировкаОшибок = 1 Тогда
глСписокНеобработанныхОшибок.ДобавитьЗначение(СообщениеОбОшибке);
// Возврат 0 блокирует вывод текста ошибки в стандартное окно сообщений
Возврат 0;
КонецЕсли;
// Возврат 1 передает ошибку стандартному обработчику платформы
Возврат 1;
КонецФункции
Теперь рассмотрим, как организовать вызов внешнего отчета в вызывающей процедуре:
глСписокНеобработанныхОшибок.УдалитьВсе();
глТрассировкаОшибок = 1;
Параметры = СоздатьОбъект("СписокЗначений");
Параметры.ДобавитьЗначение(ТекущийДокумент(), "Документ");
ОткрытьФормуМодально("Отчет", Параметры, КаталогИБ() + "ExtForms\PrnForm.ert");
глТрассировкаОшибок = 0;
Если глСписокНеобработанныхОшибок.РазмерСписка() > 0 Тогда
Предупреждение("Не удалось сформировать печатную форму из данного режима!");
КонецЕсли;
При возникновении синтаксической ошибки или ошибки исполнения внутри отчета управление вернется в вызывающую процедуру, список ошибок заполнится, а пользователь не увидит аварийных системных окон.
Выясним главную причину падения отчетов: чаще всего сбой происходит из-за попытки обратиться к свойствам формы документа через точку, когда в отчет передана обычная ссылка на объект базы данных из журнала. Чтобы предотвратить падение без изменения кода отчетов, проверим тип передаваемого значения до вызова команды открытия.
Используем встроенную функцию ТипЗначенияСтр():
ТипКонтекста = ТипЗначенияСтр(ПередаваемыйОбъект);
Если ТипКонтекста = "ГрупповойКонтекст" Тогда
// Объект является открытой формой документа
ОткрытьФормуМодально("Отчет", ПередаваемыйОбъект, ПутьКОтчету);
ИначеЕсли ТипКонтекста = "Документ" Тогда
// Объект является ссылкой на документ, полученной, например, из журнала
Предупреждение("Данная печатная форма поддерживается только из открытой формы документа!");
Иначе
Предупреждение("Некорректный параметр для печати!");
КонецЕсли;
Такой подход избавляет от необходимости перехватывать системные сбои постфактум, предотвращая саму причину их возникновения.
Если доступ к исходному коду внешних печатных форм открыт, наиболее надежным и универсальным решением будет адаптация процедуры ПриОткрытии() самого внешнего отчета. Проанализируем универсальный шаблон инициализации:
Процедура ПриОткрытии()
ВходнойПараметр = Форма.Параметр;
// Определяем, ссылка это или контекст формы
Если ТипЗначенияСтр(ВходнойПараметр) = "ГрупповойКонтекст" Тогда
Док = ВходнойПараметр.ТекущийДокумент();
ИначеЕсли ТипЗначенияСтр(ВходнойПараметр) = "Документ" Тогда
Док = ВходнойПараметр;
Иначе
Предупреждение("Не передан документ для печати!");
СтатусВозврата(0);
Возврат;
КонецЕсли;
Попытка
Печать(Док);
Исключение
Сообщить("Ошибка при формировании печатной формы: " + ОписаниеОшибки(), "!");
СтатусВозврата(0);
Возврат;
КонецПопытки;
КонецПроцедуры
В этом случае логика отчета защищена блоком Попытка — Исключение непосредственно внутри процедуры выполнения, а вызов СтатусВозврата(0) корректно прерывает открытие формы без системных аварий.
Если в системе невозможно использовать внешние компоненты, а отчет все же генерирует некритичную ошибку, скрыть следы сбоя можно штатным методом сразу после выполнения вызова:
ОткрытьФормуМодально("Отчет", Параметры, ИмяФайла);
ОчиститьОкноСообщений();
Метод ОчиститьОкноСообщений() удалит системные сообщения из окна платформы. Однако следует учитывать, что если окно сообщений уже было открыто на экране, пользователь может успеть заметить кратковременное появление текста ошибки.