При разработке универсальных обработок в среде «1С:Предприятие 7.7» программисты часто сталкиваются с жесткими ограничениями компилятора. Классический сценарий: обработка должна запускаться как в типовых, так и в нетиповых конфигурациях. В типовых решениях присутствует глобальная экспортная функция (например, глИдентификаторКонфигурации()), а в измененных или самописных базах её может не быть.
Если мы попытаемся вызвать эту функцию напрямую в коде обработки, то при запуске в нетиповой конфигурации мы получим неустранимую ошибку компиляции: «Функция не обнаружена». Модуль обработки просто откажется компилироваться и запускаться. При этом стандартная конструкция Попытка ... Исключение не спасает ситуацию, так как синтаксический анализатор проверяет код еще до выполнения.
В этой статье мы подробно разберем, как обойти жесткий синтаксический контроль платформы 1С 7.7, проверить существование глобальной экспортной процедуры или функции и, в случае её отсутствия, переопределить её логику локально внутри обработки.
Прежде чем перейти к решениям, выясним причину такого поведения платформы. Компилятор 1С 7.7 выполняет двухпроходный анализ кода перед запуском модуля. На первом проходе он строит таблицу символов, куда заносит все объявленные локальные и глобальные процедуры, функции и переменные. Если во время второго прохода компилятор встречает вызов функции, которой нет в таблице символов, процесс немедленно аварийно завершается.
Обычное экранирование вызова блоком Попытка бесполезно, так как блок обработки исключений работает на этапе выполнения (Runtime), а не на этапе компиляции (Compile-time). Нам требуются методы динамического вызова и проверки контекста.
Рассмотрим наиболее надежное и профессиональное решение, если в вашей системе допускается использование внешних компонент. Популярная библиотека 1С++ предоставляет мощный класс ВыполняемыйМодуль, который позволяет компилировать и выполнять произвольный код динамически в процессе работы.
Суть метода заключается в том, что мы пишем небольшой тестовый код, который пытается вызвать глобальную функцию, и компилируем его "на лету" с помощью 1С++. Если компиляция проходит успешно — функция существует. Если компиляция вызывает исключение — функции в глобальном контексте нет.
Разберем по шагам практическую реализацию этого метода:
Функция ПроверитьНаличиеГлобальнойФункции(ИмяФункции)
// Создаем объект динамического модуля из библиотеки 1С++
Попытка
Модуль = СоздатьОбъект("ВыполняемыйМодуль");
// Формируем строку кода для проверки
ТестовыйКод = "Процедура Тест() Результат = " + ИмяФункции + "(); КонецПроцедуры";
Модуль.УстановитьМодуль(ТестовыйКод);
Исключение
Предупреждение("Компонента 1С++ не загружена или устарела!");
Возврат 0;
КонецПопытки;
// Пытаемся скомпилировать сформированный модуль
Попытка
Модуль.КомпилироватьМодуль();
// Если ошибок нет, значит функция существует в глобальном контексте
Возврат 1;
Исключение
// Ошибка компиляции означает отсутствие функции
Возврат 0;
КонецПопытки;
КонецФункции
Имея такую проверку, мы можем безопасно вызывать локальный или глобальный вариант функции с помощью того же объекта ВыполняемыйМодуль или стандартного механизма Шаблон(). Этот способ работает безотказно и не "мусорит" в окно сообщений пользователя.
Что делать, если использование внешних компонент (таких как 1С++ или FormEx) категорически запрещено политикой безопасности компании или техническими ограничениями? Существует потрясающий, полностью штатный и "бесшумный" трюк, основанный на недокументированном поведении системного метода ОбработкаОжидания().
Метод ОбработкаОжидания(ИмяПроцедуры, Интервал) предназначен для циклического вызова процедур по таймеру. Оказывается, если передать в качестве параметра имя несуществующей процедуры, платформа не выдает критической ошибки компиляции, а просто игнорирует вызов. При этом метод ОбработкаОжидания(, 0) мгновенно останавливает таймер и возвращает имя текущей зарегистрированной процедуры.
Проанализируем функцию проверки, построенную на этом эффекте:
Функция ЕстьЛиПроцедураВКонтексте(ИмяПроцедуры)
// 1. Пытаемся проверить наличие процедуры в локальном контексте формы
Форма.ОбработкаОжидания(ИмяПроцедуры, 1);
Если Форма.ОбработкаОжидания(, 0) = ИмяПроцедуры Тогда
Возврат 1; // Процедура обнаружена в модуле формы
Иначе
// 2. Если в локальном контексте нет, проверяем глобальный контекст базы
ОбработкаОжидания(ИмяПроцедуры, 1);
Если ОбработкаОжидания(, 0) = ИмяПроцедуры Тогда
Возврат 1; // Процедура обнаружена в глобальном модуле
Иначе
Возврат 0; // Процедура отсутствует везде
КонецЕсли;
КонецЕсли;
КонецФункции
Важное примечание: Данный способ идеально подходит для процедур и функций, которые не принимают обязательных параметров. Если исследуемая процедура существует, этот код на доли секунды инициализирует таймер и тут же его останавливает, возвращая истину. При этом в табло и системный журнал не выводится никаких предупреждений об ошибках.
Еще один встроенный метод платформы — функция Шаблон(). Она позволяет динамически вычислять строковые выражения, содержащие код в квадратных скобках. Например, вызов Шаблон("[глИдентификаторКонфигурации()]") вернет результат работы функции.
Однако здесь есть два подводных камня:
Шаблон() всегда преобразуется в текстовую строку. Если проверяемая функция возвращает сложный тип данных (например, ссылку на Справочник, Документ или Таблицу значений), то мы потеряем исходный объект, получив лишь его строковое представление.Чтобы обойти проблему преобразования типов, мы можем использовать встроенную системную сериализацию через ЗначениеВСтрокуВнутр():
Функция ПолучитьЗначениеГлобальнойФункции(ИмяФункции)
Попытка
// Получаем сериализованную строку внутреннего представления объекта
СтрокаОбъекта = Шаблон("[ЗначениеВСтрокуВнутр(" + ИмяФункции + "())]");
// Восстанавливаем оригинальный объект 1С (любого типа)
Возврат ЗначениеИзСтрокиВнутр(СтрокаОбъекта);
Исключение
// Если произошла ошибка или функции не существует
Возврат ПолучитьЛокальныйИдентификатор();
КонецПопытки;
КонецФункции
Этот метод позволяет безопасно вернуть полноценный агрегатный объект из динамически вызываемой глобальной функции, если она существует.
Интересный и очень гибкий способ обхода статического синтаксического контроля — использование COM-технологий. Поскольку при обращении к внешним объектам через Automation-сервер компилятор 1С 7.7 не может проверить наличие методов "на том конце" во время компиляции, мы можем использовать обращение к текущей базе через OLE.
Для этого мы можем использовать объект V1CEnterprise.Application. Если мы подключимся к текущему сеансу, мы сможем выполнять методы глобального модуля через функцию EvalExpr (ВычислитьВыражение) OLE-объекта:
Функция ПолучитьДанныеЧерезOLE(ИмяФункции)
Попытка
// Создаем COM-объект подключения к текущей базе
// (Предполагается, что глобальная переменная OLEКоннект уже инициализирована)
Результат = OLEКоннект.EvalExpr(ИмяФункции + "()");
Возврат Результат;
Исключение
// Ошибка возникнет только на этапе выполнения, если функции нет
Возврат ПолучитьЛокальныйИдентификатор();
КонецПопытки;
КонецФункции
Этот подход гарантирует, что ваша обработка запустится при любых обстоятельствах, а отсутствие функции в глобальном модуле будет корректно обработано в блоке Исключение без аварийного завершения программы.
Мы подробно проанализировали ситуацию отсутствия динамической рефлексии в платформе «1С:Предприятие 7.7» и нашли несколько практических путей решения этой проблемы:
ВыполняемыйМодуль — это наиболее технологичное и чистое решение.ОбработкаОжидания(). Он работает незаметно для пользователя и не засоряет лог ошибками.Шаблон() и ЗначениеИзСтрокиВнутр().Выбирайте наиболее подходящий под ваши задачи инструмент и создавайте по-настоящему универсальные решения для платформы 1С 7.7!