В разработке на платформе 1С:Предприятие 8.3 (особенно в управляемых формах) программисты часто сталкиваются с проблемой "короткой жизни" серверного контекста. Все переменные, созданные в рамках одного серверного вызова &НаСервере, уничтожаются сразу после завершения процедуры. Если ваша задача требует взаимодействия с внешними системами через COM-объекты (например, V83.COMConnector, Excel или специализированные драйверы), это становится серьезным препятствием. Инициализация такого объекта может занимать несколько секунд, и повторять эту операцию при каждом нажатии кнопки пользователем — недопустимая роскошь.
В данной статье мы разберем, как "обмануть" систему и заставить COM-объект оставаться в памяти сервера, проанализируем риски каждого метода и выберем наиболее стабильное решение.
Прежде чем переходить к коду, проанализируем ситуацию. Сервер 1С работает как совокупность рабочих процессов rphost. Когда клиент делает запрос к серверу, он попадает в один из этих процессов. Как только выполнение кода завершается, 1С очищает память, выделенную под локальные переменные сеанса, чтобы освободить ресурсы для других пользователей. COM-объект является внешним по отношению к 1С, но ссылка на него хранится именно в этой очищаемой памяти.
Рассмотрим вариант, предложенный в обсуждении, который позволяет хранить ссылку на объект в течение жизни конкретной формы. Основная хитрость здесь заключается в использовании УникальныйИдентификатор формы в качестве ключа для ПоместитьВоВременноеХранилище.
Разберем пример кода, реализующий этот механизм:
&НаСервере
Функция ПолучитьСоединениеСВнешнейСистемой(НужноНовоеСоединение = Ложь)
РезультатСоединения = Неопределено;
// Пытаемся извлечь структуру с объектом из хранилища
Если Не ПустаяСтрока(Объект.АдресВХранилище) Тогда
ДанныеИзХранилища = ПолучитьИзВременногоХранилища(Объект.АдресВХранилище);
Если ТипЗнч(ДанныеИзХранилища) = Тип("Структура") Тогда
РезультатСоединения = ДанныеИзХранилища.COMОбъект;
КонецЕсли;
КонецЕсли;
// Если объекта нет или принудительно затребовано новое соединение
Если (РезультатСоединения = Неопределено) Или НужноНовоеСоединение Тогда
Попытка
Connector = Новый COMОбъект("V83.COMConnector");
ПараметрыСвязи = "Srvr='ServerName';Ref='BaseName';Usr='Login';Pwd='Password';";
РезультатСоединения = Connector.Connect(ПараметрыСвязи);
// Сохраняем объект в структуру и помещаем в хранилище
Контейнер = Новый Структура("COMОбъект", РезультатСоединения);
// Важно: привязываем к уникальному идентификатору формы
Объект.АдресВХранилище = ПоместитьВоВременноеХранилище(Контейнер, ЭтаФорма.УникальныйИдентификатор);
Исключение
ЗаписьЖурналаРегистрации("Ошибка подключения", УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки());
ВызватьИсключение "Не удалось установить COM-соединение!";
КонецПопытки;
КонецЕсли;
Возврат РезультатСоединения;
КонецФункции
Важный нюанс: 1С не умеет по-настоящему сериализовать COM-объект в строку. Она сохраняет во временном хранилище лишь IDispatch — указатель на интерфейс объекта в памяти текущего процесса rphost. Если ваш кластер состоит из нескольких рабочих процессов или серверов, и следующий вызов попадет на другой процесс, вы получите ошибку "Объект не найден", так как в памяти другого процесса по этому адресу ничего нет.
Это один из самых элегантных способов кэширования на стороне сервера. Мы создаем общий модуль, устанавливаем у него свойство Повторное использование возвращаемых значений в значение "На время сеанса" или "На время вызова".
Рассмотрим, как это работает:
КэшВнешнихСоединенийСервер.Повторное использование: На время сеанса.
Функция ПолучитьАктивныйCOMОбъект() Экспорт
// Этот код выполнится один раз за 20 минут (по умолчанию)
// или пока память процесса не будет переполнена
Connector = Новый COMОбъект("V83.COMConnector");
Возврат Connector.Connect("СтрокаСоединения");
КонецФункции
Проанализируем ситуацию: платформа сама управляет кэшем. Если вы вызываете эту функцию из любого места кода, 1С сначала проверит, нет ли уже готового результата в памяти текущего процесса для вашего сеанса. Преимущество: автоматическая очистка и высокая скорость. Недостаток: та же проблема с многопроцессными кластерами — кэш живет внутри одного rphost.
Если COM-объект крайне нестабилен или его инициализация критически замедляет работу интерфейса, мы можем применить архитектурный паттерн "Очередь задач".
Выясним причину, почему это эффективно: мы разделяем пользовательский интерфейс и тяжелую логику внешнего объекта. Пользователь лишь фиксирует намерение (записывает данные в регистр), а специально выделенный процесс эти данные обрабатывает.
Разберем шаги реализации:
ОчередьЗадачCOM с полями: ПараметрыЗапуска, Статус, Результат.
Процедура ОбработкаОчередиCOM() Экспорт
// Инициализируем объект один раз
ВнешнийОбъект = Новый COMОбъект("SomeHeavyLibrary.Application");
Пока Истина Цикл
ЗаписьОчереди = ВыбратьПервуюСвободнуюЗапись();
Если ЗаписьОчереди = Неопределено Тогда
Приостановить(2); // Ждем новые задачи, не нагружая процессор
Продолжить;
КонецЕсли;
// Выполняем действия с объектом
Результат = ВнешнийОбъект.DoWork(ЗаписьОчереди.Параметры);
СохранитьРезультат(ЗаписьОчереди, Результат);
КонецЦикла;
КонецПроцедуры
Этот метод гарантирует, что COM-объект "висит" в памяти столько, сколько живет фоновое задание. Это самый стабильный вариант для высоконагруженных систем.
Если вы опытный администратор или разработчик, вы можете вынести инициализацию объекта за пределы 1С. Зарегистрируйте вашу DLL в службах компонентов Windows (COM+). В настройках приложения COM+ укажите тип активации Server Application. Это заставит систему запускать объект в отдельном процессе dllhost.exe. Теперь, даже если 1С завершит свой процесс или сеанс, сам COM-объект может оставаться активным в памяти Windows, ожидая новых подключений.
Проанализировав все доступные варианты, мы можем составить краткий гид по выбору решения:
УникальныйИдентификатор формы. Это позволит "таскать" объект за собой, пока открыто окно.Используя предложенные методики, вы сможете значительно повысить отзывчивость интерфейса для конечных пользователей, избавив их от многосекундных ожиданий при каждом взаимодействии с сервером.