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