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