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