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