При разработке сложных систем или тиражных решений на платформе 1С:Предприятие 8.3 программисты часто сталкиваются с необходимостью динамического вызова функций из общих модулей. Ситуация осложняется, когда конфигурации различаются: в одних модули помечены как Глобальные, в других — нет, а в третьих нужный модуль и вовсе может отсутствовать (например, при попытке написать универсальную обработку для баз с БСП и без неё). В этой статье мы подробно разберем, как программно получить контекст общего модуля, имея лишь его строковое имя, и как правильно обрабатывать такие вызовы.
Первое, с чем сталкиваются разработчики — это путаница между объектом метаданных и программным контекстом модуля. Проанализируем типичную ошибку. Если мы попытаемся обратиться к модулю через коллекцию метаданных:
ОбъектМетаданных = Метаданные.ОбщиеМодули.РегламентированнаяОтчетность;
// Попытка вызвать функцию приведет к ошибке:
ОбъектМетаданных.НужнаяФункция();
Выясним причину: Метаданные.ОбщиеМодули возвращает объект типа ОбъектМетаданных: Общий модуль. Этот объект содержит информацию о свойствах модуля (имя, синоним, флаги «Глобальный», «Сервер» и т.д.), но он не содержит самих экспортных процедур и функций. Для вызова кода нам нужен именно контекст исполнения.
Самый прямой и эффективный способ получить ссылку на контекст модуля по его имени — использование встроенной функции Вычислить() (или Eval() в англоязычном варианте). Рассмотрим простейший пример:
ИмяМодуля = "БухгалтерскийУчетПереопределяемый";
// Получаем контекст модуля
МодульКонтекст = Вычислить(ИмяМодуля);
// Теперь мы можем вызывать его методы через точку
МодульКонтекст.ОбновитьПараметры();
Разберем, почему это работает. Функция Вычислить() принимает строку и интерпретирует её как фрагмент кода на языке 1С. Поскольку имя общего модуля (если он не глобальный) является доступным идентификатором в глобальном контексте, платформа возвращает ссылку на сам объект модуля.
В реальных задачах мы не всегда можем быть уверены, что модуль с конкретным именем существует в текущей конфигурации. Чтобы избежать исключений, проанализируем ситуацию и добавим проверку через метаданные. Это позволит коду работать стабильно в разных редакциях программ.
Функция ПолучитьМодульБезопасно(ИмяМодуля) Экспорт
// Проверяем наличие модуля в дереве конфигурации
Если Метаданные.ОбщиеМодули.Найти(ИмяМодуля) <> Неопределено Тогда
Возврат Вычислить(ИмяМодуля);
Иначе
Возврат Неопределено;
КонецЕсли;
КонецФункции
Посмотрим на пример использования этой функции:
МодульБСП = ПолучитьМодульБезопасно("ОбщегоНазначения");
Если МодульБСП <> Неопределено Тогда
Значение = МодульБСП.ЗначениеРеквизитаОбъекта(Ссылка, "Код");
КонецЕсли;
Если ваша конфигурация построена на базе БСП, то изобретать велосипед не нужно. В составе библиотеки уже есть готовый инструмент в модуле ОбщегоНазначения. Рассмотрим подробнее метод ОбщийМодуль():
// В БСП это реализовано максимально надежно
НужныйМодуль = ОбщегоНазначения.ОбщийМодуль("РаботаСДоговорами");
НужныйМодуль.ПроверитьСтатус(ДоговорСсылка);
Важный нюанс: стандартная функция БСП может вызвать исключение, если модуль не найден. Если вы пишете код для внешних отчетов, которые должны работать «везде», лучше использовать комбинацию проверки метаданных и вызова Вычислить().
Рассмотрим сложную ситуацию: вы создаете внешнюю обработку, которая должна регистрироваться в дополнительных отчетах и обработках через интерфейс БСП (функция СведенияОВнешнейОбработке). Однако вы хотите, чтобы эта же обработка открывалась и работала в старых конфигурациях, где модулей БСП вообще нет.
Проблема в том, что 1С проверяет синтаксис при компиляции. Если в коде есть обращение к ОбщегоНазначения.ЧтоТоТам(), а модуля ОбщегоНазначения в базе нет, обработка просто не откроется. Выясним, как обмануть компилятор, используя особенность двухпроходной проверки:
Функция СведенияОВнешнейОбработке() Экспорт
// Обман компилятора: создаем локальные переменные с именами общих модулей
Если Ложь Тогда
ОбщегоНазначения = Неопределено;
ДополнительныеОтчетыИОбработки = Неопределено;
КонецЕсли;
// Теперь компилятор считает, что ОбщегоНазначения - это переменная,
// и не ищет общий модуль при проверке синтаксиса
ПараметрыРегистрации = Новый Структура;
// ... заполнение параметров ...
Возврат ПараметрыРегистрации;
КонецФункции
В блоке Если Ложь Тогда мы объявляем идентификаторы, совпадающие с именами отсутствующих модулей. Это позволяет коду пройти проверку синтаксиса, а в режиме исполнения управление по этим веткам никогда не пройдет, либо вы сможете использовать Попытка...Исключение для динамической проверки.
Проанализируем важный аспект работы в облачных сервисах, таких как 1С:Фреш. Использование функции Вычислить() является потенциальной уязвимостью. Если вы передаете в эту функцию строку, сформированную пользователем, это может привести к выполнению произвольного вредоносного кода.
Основные правила безопасности при использовании динамических модулей:
Вычислить() данные, полученные из полей ввода или внешних файлов без жесткой проверки.Метаданные.ОбщиеМодули.Вычислить("ИмяМодуля") на клиенте вернет ошибку «Переменная не определена».Вычислить() на каждой итерации.Подводя итог, можно сказать, что механизм получения модуля по имени через Вычислить() является мощным инструментом метапрограммирования в 1С. Он позволяет создавать по-настоящему гибкие и универсальные решения, способные адаптироваться к изменяющейся среде метаданных конфигурации.