Что спрятано в реквизите типа ХранилищеЗначения и как это прочитать

· 9 мин

Содержание

Посмотреть хранилище значений 1С можно только одним способом: вызвать у него метод Получить() и изучить то, что вернётся. В отладчике это делается через окно вычисления выражения (Shift+F9) с выражением вида Ссылка.ИмяРеквизита.Получить(). Без отладчика — внешней обработкой, которая читает реквизит на сервере и выводит результат в сообщения.

Какой способ выбрать, зависит от задачи. Разово заглянуть в один объект — отладчик. Проверить сотни элементов справочника или регистра — запрос плюс разбор в коде. Найти, где в конфигурации вообще хранятся такие данные, — обход метаданных. Все три варианта ниже, с готовым кодом.

Почему содержимое хранилища значений не видно в форме и консоли запросов

ХранилищеЗначения — это контейнер. Внутрь кладут любое сериализуемое значение платформы, а в базе данных оно лежит одним двоичным полем. Поэтому:

  • 📦 в списке и в консоли запросов вместо данных видно только слово ХранилищеЗначения;
  • 🔒 в управляемой форме реквизит объекта такого типа не попадает в данные формы, его нельзя просто вывести на форму;
  • 🔍 по содержимому нельзя отбирать, группировать и сортировать в запросе.

Что обычно кладут в хранилище:

Что хранитсяТипичный пример
Структура, Соответствиепараметры обмена, настройки обработки
ТаблицаЗначений, Массивсохранённый список строк, кэш расчёта
ДвоичныеДанныефайл, вложение, сертификат
Картинкафото сотрудника, логотип организации
ТабличныйДокументсохранённая печатная форма
НастройкиКомпоновкиДанныхвариант отчёта пользователя

Хранилище создают так: Новый ХранилищеЗначения(Значение) или со сжатием — Новый ХранилищеЗначения(Значение, Новый СжатиеДанных(9)). Уровень сжатия задаётся числом от 0 до 9. При чтении сжатие снимается автоматически, Получить() вызывают одинаково.

Получить() возвращает копию значения. Если изменить полученную структуру, в базе ничего не поменяется. Чтобы сохранить изменения, создайте новое хранилище, присвойте его реквизиту и запишите объект.

Как посмотреть хранилище значений в отладчике

Это самый быстрый путь, если база открыта в Конфигураторе или в EDT и есть права на отладку.

  1. Откройте модуль, где объект с хранилищем уже доступен: модуль объекта, модуль формы в серверной процедуре или общий модуль.
  2. Поставьте точку останова (F9) на строке, где есть ссылка или объект.
  3. Запустите отладку (F5) и выполните действие, которое вызывает этот код.
  4. Когда выполнение остановится, нажмите Shift+F9 — откроется окно вычисления выражения.
  5. Введите выражение, например Объект.ДанныеНастроек.Получить() или Ссылка.ДанныеНастроек.Получить(), и нажмите «Рассчитать».

Если внутри структура, массив или таблица значений, отладчик покажет их разворачиваемым деревом: ключи, колонки, строки. Для примитивов результат будет в поле значения сразу.

Сам реквизит без Получить() в отладчике разворачивать бесполезно — будет виден только тип. Это частая причина вопроса «отладчик ничего не показывает».

Если внутри картинка, файл или табличный документ

Отладчик не отрисует картинку и не откроет печатную форму. Удобнее выгрузить содержимое в файл прямо из окна выражения:

  • ДвоичныеДанные — выражение Ссылка.Файл.Получить().Записать("C:\Temp\file.bin");
  • Картинка — Ссылка.Фото.Получить().ПолучитьДвоичныеДанные().Записать("C:\Temp\photo.png");
  • ТабличныйДокумент — Ссылка.Макет.Получить().Записать("C:\Temp\form.mxl").

Файл появится на том компьютере, где выполняется код. Если точка останова в серверной процедуре клиент-серверной базы, ищите файл на сервере 1С, а не у себя.

Вывод содержимого хранилища значений кодом

Когда отладчика нет под рукой, например в рабочей базе без прав на Конфигуратор, хватит внешней обработки. Создайте управляемую форму, добавьте реквизит формы Ссылка нужного типа и команду. Ниже серверная часть, которая разбирает значение на любую глубину и выводит его в окно сообщений.

&НаСервереБезКонтекста
Процедура ПоказатьХранилище(Ссылка, ИмяРеквизита)
	Хранилище = Ссылка[ИмяРеквизита];
	Если ТипЗнч(Хранилище) <> Тип("ХранилищеЗначения") Тогда
		Сообщить("Реквизит не является хранилищем значений");
		Возврат;
	КонецЕсли;
	Значение = Хранилище.Получить();
	Сообщить("Тип содержимого: " + Строка(ТипЗнч(Значение)));
	ВывестиЗначение(Значение, "");
КонецПроцедуры

&НаСервереБезКонтекста
Процедура ВывестиЗначение(Значение, Отступ)
	ТипЗначения = ТипЗнч(Значение);
	Если ТипЗначения = Тип("Структура") Или ТипЗначения = Тип("ФиксированнаяСтруктура")
		Или ТипЗначения = Тип("Соответствие") Или ТипЗначения = Тип("ФиксированноеСоответствие") Тогда
		Для Каждого КлючИЗначение Из Значение Цикл
			Сообщить(Отступ + Строка(КлючИЗначение.Ключ) + " (" + Строка(ТипЗнч(КлючИЗначение.Значение)) + ")");
			ВывестиЗначение(КлючИЗначение.Значение, Отступ + "    ");
		КонецЦикла;
	ИначеЕсли ТипЗначения = Тип("Массив") Или ТипЗначения = Тип("ФиксированныйМассив") Тогда
		Для Индекс = 0 По Значение.ВГраница() Цикл
			Сообщить(Отступ + "[" + Формат(Индекс, "ЧН=0; ЧГ=0") + "]");
			ВывестиЗначение(Значение[Индекс], Отступ + "    ");
		КонецЦикла;
	ИначеЕсли ТипЗначения = Тип("ТаблицаЗначений") Тогда
		Сообщить(Отступ + "Таблица, строк: " + Формат(Значение.Количество(), "ЧН=0; ЧГ=0"));
		Для Каждого СтрокаТаблицы Из Значение Цикл
			Части = Новый Массив;
			Для Каждого Колонка Из Значение.Колонки Цикл
				Части.Добавить(Колонка.Имя + " = " + Строка(СтрокаТаблицы[Колонка.Имя]));
			КонецЦикла;
			Сообщить(Отступ + СтрСоединить(Части, "; "));
		КонецЦикла;
	ИначеЕсли ТипЗначения = Тип("ДвоичныеДанные") Тогда
		Сообщить(Отступ + "Двоичные данные, байт: " + Формат(Значение.Размер(), "ЧН=0; ЧГ=0"));
	Иначе
		Сообщить(Отступ + Строка(Значение));
	КонецЕсли;
КонецПроцедуры

Вызов с клиента: ПоказатьХранилище(Ссылка, "ДанныеНастроек"). Имя реквизита подставьте своё.

Как читать результат:

  • ✅ «Тип содержимого: Структура» и список ключей — всё прочиталось;
  • ⚪ «Тип содержимого: Неопределено» — хранилище пустое или туда явно записали Неопределено;
  • 📏 большой размер двоичных данных — внутри файл, его удобнее выгрузить через Записать().

Данные в хранилище нередко содержат пароли, токены обменов и персональные данные. Не выводите такие значения в сообщения в рабочей базе при посторонних и не сохраняйте их в незащищённых файлах.

Массовый анализ хранилищ через запрос и временные таблицы

Отладчик и сообщения хороши для одного объекта. Если нужно понять, у каких из 3 000 элементов справочника в хранилище лежит структура с нужным ключом, придётся совмещать запрос и код.

Схема из трёх шагов:

  1. Запросом отберите объекты по обычным полям и получите поле хранилища в выборку.
  2. В цикле вызовите Получить() и разложите нужные свойства в таблицу значений с типизированными колонками.
  3. Передайте таблицу параметром обратно в запрос, поместите во временную таблицу и анализируйте как обычные данные: группируйте, соединяйте, считайте.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|	Т.Ссылка КАК Ссылка,
|	Т.ДанныеНастроек КАК Хранилище
|ИЗ
|	Справочник.НастройкиОбмена КАК Т
|ГДЕ
|	НЕ Т.ПометкаУдаления";
Выборка = Запрос.Выполнить().Выбрать();

Итог = Новый ТаблицаЗначений;
Итог.Колонки.Добавить("Ссылка", Новый ОписаниеТипов("СправочникСсылка.НастройкиОбмена"));
Итог.Колонки.Добавить("ТипСодержимого", Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(100)));
Итог.Колонки.Добавить("ЕстьКлюч", Новый ОписаниеТипов("Булево"));

Пока Выборка.Следующий() Цикл
	Значение = Выборка.Хранилище.Получить();
	НоваяСтрока = Итог.Добавить();
	НоваяСтрока.Ссылка = Выборка.Ссылка;
	НоваяСтрока.ТипСодержимого = Строка(ТипЗнч(Значение));
	НоваяСтрока.ЕстьКлюч = ТипЗнч(Значение) = Тип("Структура") И Значение.Свойство("АдресСервера");
КонецЦикла;

Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
Запрос.УстановитьПараметр("Итог", Итог);
Запрос.Текст =
"ВЫБРАТЬ
|	И.Ссылка КАК Ссылка,
|	И.ТипСодержимого КАК ТипСодержимого,
|	И.ЕстьКлюч КАК ЕстьКлюч
|ПОМЕСТИТЬ ВТ_Разбор
|ИЗ
|	&Итог КАК И
|;
|ВЫБРАТЬ
|	ВТ.ТипСодержимого КАК ТипСодержимого,
|	КОЛИЧЕСТВО(ВТ.Ссылка) КАК Объектов
|ИЗ
|	ВТ_Разбор КАК ВТ
|СГРУППИРОВАТЬ ПО
|	ВТ.ТипСодержимого";

Справочник НастройкиОбмена, реквизит ДанныеНастроек и ключ АдресСервера — условные, замените своими.

Почему колонки типизированы: запрос принимает таблицу значений параметром только с явно заданными типами колонок. Строку берите ограниченной длины, иначе по ней не получится сгруппировать.

Если объектов десятки тысяч, читайте порциями: отбирайте по 1 000 ссылок через ПЕРВЫЕ с условием на ссылку больше последней обработанной. Иначе выборка с тяжёлыми двоичными полями съест память рабочего процесса.

Ключи настроек, владельцы и безопасное хранилище

Часть данных лежит не в реквизитах ваших объектов, а в механизмах, где доступ идёт по ключам. Здесь Получить() за вас вызывает платформа или библиотека, но смысл тот же.

Хранилища настроек платформы

Настройки форм, отчётов и пользовательские параметры платформа хранит в системных хранилищах. Значение адресуется тремя ключами:

ПараметрЧто означает
КлючОбъектак чему относится настройка: форма, отчёт, условное имя подсистемы
КлючНастроеккакая именно настройка у этого объекта
ИмяПользователячья настройка; если не указать — текущего пользователя

Прочитать можно так: ХранилищеОбщихНастроек.Загрузить("МояОбработка", "ПоследниеПараметры"). Результат сразу готов к разбору процедурой ВывестиЗначение из примера выше. Для вариантов отчётов есть отдельное ХранилищеВариантовОтчетов, для пользовательских настроек отчётов — ХранилищеПользовательскихНастроекОтчетов.

Безопасное хранилище БСП

В типовых конфигурациях на БСП пароли и токены не держат в открытых реквизитах. Их пишут в безопасное хранилище, привязывая к владельцу — ссылке на объект или строковому идентификатору.

  • 📖 чтение: ОбщегоНазначения.ПрочитатьДанныеИзБезопасногоХранилища(Владелец, "Пароль");
  • ✏️ запись: ОбщегоНазначения.ЗаписатьДанныеВБезопасноеХранилище(Владелец, Данные, "Пароль");
  • 🗑️ удаление: ОбщегоНазначения.УдалитьДанныеИзБезопасногоХранилища(Владелец).

Если ключ один, функция чтения вернёт само значение, если передать несколько ключей через запятую — структуру. Вызывать её нужно на сервере в привилегированном режиме: перед вызовом УстановитьПривилегированныйРежим(Истина), после — УстановитьПривилегированныйРежим(Ложь). Без этого библиотека выдаст исключение.

Пространство имён при сериализации

Хранилище значений можно передать во внешнюю систему или выгрузить в XML. В XDTO у него тип ValueStorage из пространства имён http://v8.1c.ru/8.1/data/core, содержимое пишется строкой base64. Отсюда простой способ оценить размер: длина XMLСтрока(Хранилище) примерно на треть больше объёма упакованных данных.

Где в конфигурации искать реквизиты типа ХранилищеЗначения

Иногда неизвестно, какой реквизит вообще хранит данные. Обход метаданных выдаст полный список за пару секунд.

&НаСервереБезКонтекста
Процедура НайтиХранилища()
	ТипХранилища = Тип("ХранилищеЗначения");
	Коллекции = Новый Массив;
	Коллекции.Добавить(Метаданные.Справочники);
	Коллекции.Добавить(Метаданные.Документы);
	Коллекции.Добавить(Метаданные.РегистрыСведений);
	Для Каждого Коллекция Из Коллекции Цикл
		Для Каждого ОбъектМД Из Коллекция Цикл
			Если Метаданные.РегистрыСведений.Содержит(ОбъектМД) Тогда
				Поля = ОбъектМД.Ресурсы;
			Иначе
				Поля = ОбъектМД.Реквизиты;
			КонецЕсли;
			Для Каждого Поле Из Поля Цикл
				Если Поле.Тип.СодержитТип(ТипХранилища) Тогда
					Сообщить(ОбъектМД.ПолноеИмя() + "." + Поле.Имя);
				КонецЕсли;
			КонецЦикла;
		КонецЦикла;
	КонецЦикла;
КонецПроцедуры

Пример проверяет реквизиты справочников и документов и ресурсы регистров сведений. Табличные части, измерения и реквизиты регистров при необходимости добавьте тем же приёмом.

В Конфигураторе то же видно глазами: откройте свойства реквизита и посмотрите поле «Тип». Но на конфигурации из тысяч объектов код быстрее.

Ошибки при чтении хранилища значений

Чаще всего проблема не в самом хранилище, а в контексте вызова или в ожиданиях от результата.

Диагностика: выберите симптом

Очистка хранилищ и рост базы

Хранилища значений — частая причина раздутой базы. Кэши, логи обменов и старые вложения копятся годами, а в списках их не видно.

Порядок очистки:

  1. Найдите реквизиты-хранилища обходом метаданных из раздела выше.
  2. Оцените объём: в цикле по выборке сложите длины XMLСтрока(Выборка.Хранилище) и сравните объекты между собой.
  3. Решите, что можно удалить: кэш расчёта и устаревшие логи — да; вложения, фото и настройки обменов — только после согласования с владельцем данных.
  4. Очистите: присвойте реквизиту Новый ХранилищеЗначения(Неопределено) и запишите объект. Для безопасного хранилища вызывайте функцию удаления БСП.

Перед массовой очисткой сделайте резервную копию базы. Удалённое содержимое хранилища восстановить нечем, кроме копии.

После очистки размер файла базы сам не уменьшится. В файловой базе место возвращает сжатие таблиц в режиме «Тестирование и исправление» Конфигуратора. Для этого нужен монопольный доступ: все пользователи должны выйти из базы, поэтому запускайте процедуру вне рабочего времени. В клиент-серверной базе место освобождает уже администратор СУБД своими средствами.

Чтобы база не росла снова, при разработке задайте хранилищам сжатие (Новый СжатиеДанных(9)) и предусмотрите регламентное задание, которое чистит кэши старше заданного срока.

Частые вопросы

Можно ли посмотреть хранилище значений без Конфигуратора?
Да. Подойдёт внешняя обработка с кодом из статьи, открытая в режиме «1С:Предприятие». Нужны права на открытие внешних обработок и на чтение объекта.
Сжатое хранилище читается по-другому?
Нет. Метод Получить() сам распаковывает данные, независимо от уровня сжатия при записи.
Можно ли прочитать хранилище значений прямо в SQL?
Нет. В таблице СУБД лежит двоичное поле во внутреннем формате платформы. Читать его нужно средствами 1С.
Как скопировать содержимое хранилища в другую базу?
Выгрузите объект вместе с реквизитом через обмен данными или XML-сериализацию. Хранилище переносится целиком, как строка base64.
Видны ли данные хранилища пользователю без прав администратора?
Если у пользователя есть право чтения объекта и доступ к коду, который вызывает Получить(), — да. Безопасное хранилище БСП читается только в привилегированном режиме.