При работе с расширениями в 1С:Предприятии, особенно когда мы используем толстый клиент, часто возникает ситуация, когда расширение "не видит" общие модули основной конфигурации или даже другие общие модули самого расширения. Эта проблема может поставить нас в тупик, но, как мы выясним, она обычно связана с пониманием контекста выполнения кода и правильным использованием директив компиляции и механизма вызова сервера.
Давайте вместе разберемся в причинах этой проблемы и рассмотрим подробные решения, которые помогут нам наладить корректную работу.
Прежде чем перейти к решениям, проанализируем ситуацию и выясним, почему возникает такая невидимость. Корень проблемы кроется в различиях между клиентским и серверным контекстом выполнения кода в 1С, а также в специфике работы толстого клиента в режиме управляемого приложения.
Контекст выполнения и свойства общих модулей:
В 1С код может выполняться в разных контекстах: на клиенте (тонкий или толстый клиент, веб-клиент), на сервере или во внешнем соединении. Общие модули имеют специальные свойства, которые определяют, в каком контексте они будут доступны:
Клиент (управляемое приложение): Код модуля доступен на клиенте.Сервер: Код модуля доступен на сервере.Внешнее соединение: Код модуля доступен при работе через внешнее соединение.Если общий модуль предназначен только для выполнения на сервере (установлен флаг Сервер), а мы пытаемся вызвать его функцию из клиентского контекста (например, из модуля формы или даже из некоторых модулей, которые могут выполняться на клиенте в толстом клиенте), то без соответствующей "прослойки" или настройки модуль будет невидим.
Директивы компиляции:
Для явного разграничения кода, который должен выполняться только на сервере или только на клиенте, используются директивы препроцессора. Мы можем встретить такие конструкции, как #Если НаСервере Тогда ... #КонецЕсли или #Если НаКлиенте Тогда ... #КонецЕсли. Если в общем модуле или в вызывающем его коде используются такие директивы, и они исключают текущий контекст выполнения, то код не будет скомпилирован и, соответственно, будет недоступен.
Особенности толстого клиента и модулей менеджеров:
В клиент-серверной архитектуре и управляемом режиме, код модулей менеджеров, модулей объектов и модулей сеанса по умолчанию компилируется и выполняется на сервере. Однако в толстом клиенте, работающем в режиме управляемого приложения, эти модули могут начать компилироваться и даже частично выполняться на стороне клиента. Это может привести к ошибкам, если в таких модулях есть прямые обращения к чисто серверным общим модулям без использования механизма вызова сервера.
Рассмотрим, как мы можем решить эту проблему, используя информацию из форума и дополнительные знания.
Это решение является прямым ответом на проблему, описанную автором темы на форуме, и представляет собой наиболее надежный способ обеспечить доступ к серверным функциям из клиентского или "промежуточного" контекста в расширении. Разберем его по шагам.
Ситуация: Изначально у нас есть процедура в модуле менеджера справочника (или любом другом модуле, который может выполняться на клиенте в толстом клиенте), которая пытается вызвать функцию из серверного общего модуля. Поскольку модуль менеджера справочника в толстом клиенте может оказаться в клиентском контексте, прямой вызов серверной функции становится невозможным.
Создаем новый общий модуль в расширении:
Мы создадим новый общий модуль непосредственно в нашем расширении. Назовем его, например, _НашеРасширениеСерверныеВызовы.
Важный момент: Для этого модуля необходимо установить свойство Вызов сервера. Это свойство позволяет экспортируемым процедурам и функциям этого общего модуля быть вызванными с клиента, при этом их выполнение будет происходить на сервере.
Убедимся также, что для этого модуля установлен флаг Сервер, чтобы его код компилировался и был доступен на сервере.
Создаем функцию-посредник в новом общем модуле:
В созданном общем модуле мы определим новую функцию, которая будет являться "прослойкой" или "посредником" для вызова нашей исходной серверной функции. Эта функция будет объявлена как экспортная, чтобы ее можно было вызвать из других модулей.
Посмотрим на пример кода:
// В общем модуле расширения _НашеРасширениеСерверныеВызовы
// Свойства модуля: Сервер - Да, Вызов сервера - Да
Функция ВызватьСервернуюФункциюРасширения(Параметр1, Параметр2) Экспорт
// Здесь мы вызываем ту самую серверную функцию,
// к которой не было прямого доступа из модуля менеджера.
// Предположим, она находится в ОбщемМодулеОсновнойКонфигурацииСервер.
Возврат ОбщийМодульОсновнойКонфигурацииСервер.НашаСервернаяФункция(Параметр1, Параметр2);
КонецФункции
Обратите внимание, что внутри этой функции мы уже находимся на сервере (благодаря свойству Вызов сервера), и можем без проблем обращаться к любым другим серверным общим модулям.
Изменяем вызов в исходном модуле:
Теперь в нашем модуле менеджера справочника (или другом проблемном модуле) мы будем вызывать не напрямую исходную серверную функцию, а нашу новую функцию-посредник из общего модуля расширения.
Пример изменения кода:
// В модуле менеджера справочника (или другом клиентском/промежуточном модуле)
Процедура ПроцедураМодуляМенеджера() Экспорт
// ... какой-то код ...
// Вместо прямого вызова ОбщийМодульОсновнойКонфигурацииСервер.НашаСервернаяФункция(...)
// Теперь вызываем функцию-посредник из расширения
Результат = _НашеРасширениеСерверныеВызовы.ВызватьСервернуюФункциюРасширения(Значение1, Значение2);
// ... дальнейший код ...
КонецПроцедуры
Таким образом, мы создали надежный мост между потенциально клиентским контекстом и чисто серверным кодом, используя механизм Вызова сервера. Это является основным решением, которое привел автор темы, и оно успешно решает проблему.
Как мы уже упоминали, директивы компиляции играют ключевую роль в определении того, какой код будет доступен в том или ином контексте. Если общие модули не видны, нам следует внимательно проверить, нет ли в них или в вызывающем коде директив, которые исключают текущий контекст выполнения.
Анализируем директивы в общих модулях:
Откроем проблемные общие модули и посмотрим, какие директивы в них используются. Например, если функция обернута в #Если НаСервере Тогда ... #КонецЕсли, а мы пытаемся вызвать ее с клиента без механизма вызова сервера, то она будет недоступна.
Используем директивы для разграничения контекстов:
Если нам нужно, чтобы часть кода выполнялась только на сервере, а другая — только на клиенте, мы должны явно это указать:
// Пример использования директив в общем модуле
#Если НаСервере Тогда
Процедура СервернаяПроцедура() Экспорт
// Этот код будет скомпилирован и доступен только на сервере
Сообщить("Выполняется на сервере");
КонецПроцедуры
#КонецЕсли
#Если НаКлиенте Тогда
Процедура КлиентскаяПроцедура() Экспорт
// Этот код будет скомпилирован и доступен только на клиенте
Сообщить("Выполняется на клиенте");
КонецПроцедуры
#КонецЕсли
Специфические директивы для модулей менеджеров в толстом клиенте:
Для модулей менеджеров, модулей объектов и модулей сеанса, чтобы гарантировать их выполнение на сервере (даже в толстом клиенте), мы можем использовать более сложные директивы, исключающие клиентский контекст. Это особенно актуально, если в них есть обращения к чисто серверным функциям.
Пример:
// В модуле менеджера справочника
#Если Сервер Или ТолстыйКлиентОбычноеПриложение Или ВнешнееСоединение Тогда
// Весь код модуля, который должен выполняться на сервере
Процедура ОбработатьДанныеНаСервере() Экспорт
// ... вызовы серверных общих модулей ...
КонецПроцедуры
#Иначе
// Этот блок будет скомпилирован, если модуль вдруг попытается
// выполниться в чисто клиентском контексте (например, тонкий клиент),
// что для модуля менеджера обычно недопустимо.
ВызватьИсключение НСтр("ru = 'Недопустимый вызов объекта на клиенте.'");
#КонецЕсли
Такой подход позволяет нам жестко контролировать контекст выполнения и избегать неожиданных ошибок.
Мы должны убедиться, что свойства целевого общего модуля (того, который расширение не видит) настроены корректно в основной конфигурации.
Свойство Сервер: Если функция общего модуля должна выполняться на сервере, то флаг Сервер для этого модуля должен быть установлен. Без этого флажка модуль не будет скомпилирован на сервере, и соответственно, серверные вызовы к нему будут невозможны.
Свойство Клиент (управляемое приложение): Если нам нужен доступ к функциям общего модуля непосредственно с клиента (без вызова сервера), то флаг Клиент (управляемое приложение) должен быть установлен.
Свойство Вызов сервера: Как мы уже рассмотрели в Решении 1, если мы хотим вызывать серверные функции из клиентского кода, используя общий модуль как посредник, то для этого общего модуля (или для общего модуля, содержащего целевую серверную функцию, если он сам является точкой входа) обязательно должен быть установлен флаг Вызов сервера.
Помимо основных решений, рассмотрим несколько важных моментов, которые также могут влиять на видимость общих модулей в расширениях:
Безопасный режим расширения:
Иногда проблемы с видимостью модулей расширения, или с выполнением определенных функций, могут быть связаны с тем, что расширение подключено в безопасном режиме. Для работы некоторых функций или доступа к определенным объектам может потребоваться снять флажок "Безопасный режим" в свойствах расширения. Мы должны быть внимательны и понимать, что отключение безопасного режима может нести определенные риски безопасности, поэтому использовать его нужно осознанно.
Ограничения при создании собственных общих модулей в расширениях:
Мы можем создавать собственные общие модули в расширении. Однако следует помнить об ограничениях: они не должны быть глобальными серверными и не должны быть привилегированными. Также мы не можем заимствовать глобальные серверные модули из основной конфигурации. Код из расширения по умолчанию будет исполняться в непривилегированном режиме, если иное не разрешено в профиле безопасности.
Расширение существующих модулей с помощью аннотаций:
Если наша задача состоит в том, чтобы изменить поведение существующей процедуры или функции в модуле основной конфигурации, мы можем заимствовать этот модуль в расширение и использовать аннотации &Перед, &Вместо, &После. Это позволяет нам встраивать свой код до, вместо или после выполнения типовой процедуры/функции без изменения исходной конфигурации:
// В заимствованном модуле (например, ОбщийМодульОсновнойКонфигурацииКлиент)
&Перед Процедура ТиповаяПроцедура(Параметр)
// Здесь мы можем выполнить свой код до вызова типовой процедуры
Сообщить("Свой код ПЕРЕД типовой процедурой");
КонецПроцедуры
&После Процедура ТиповаяПроцедура(Параметр)
// Здесь мы можем выполнить свой код после вызова типовой процедуры
Сообщить("Свой код ПОСЛЕ типовой процедуры");
КонецПроцедуры
// &Вместо Процедура ТиповаяПроцедура(Параметр)
// // Здесь мы полностью замещаем типовую процедуру своим кодом
// Сообщить("Свой код ВМЕСТО типовой процедуры");
// КонецПроцедуры
Этот механизм, хоть и не напрямую связан с "невидимостью" общих модулей, является мощным инструментом расширений и позволяет нам эффективно взаимодействовать с кодом типовых конфигураций.
Мы рассмотрели основные причины, по которым расширение в толстом клиенте может "не видеть" общие модули, и предложили несколько подходов к решению этой проблемы. Главное – это понимание контекста выполнения кода и правильное использование механизмов 1С для взаимодействия между клиентом и сервером. Применяя эти знания, мы сможем эффективно работать с расширениями и избегать подобных затруднений.
← К списку