Глобальные переменные окружения – делаем своими руками

28 9

Уже довольно давно я периодически слышу о потребности иметь в системе DIRECTUM глобальные переменные окружения, доступные из прикладной разработки. На этот счет зарегистрирована пока не реализованная идея. А совсем недавно тема поднималась на форуме. Но на самом деле такие переменные можно сделать своими руками, и в этой статье я хочу поделиться готовой реализацией.

Идея реализации – проста до безобразия

Глобальное окружение представляет собой COM-сервер. В любом вычислении на ISBL мы можем к нему обратиться – положить туда нужные переменные, или получить оттуда переменные, которые сложили туда ранее в других вычислениях.

Установка и удаление

Прежде чем пользоваться окружением – его нужно установить. Для установки надо скопировать файл SBRteEnvironment.exe из приложенного архива SBRteEnvironment.zip (230,11 Кб) на целевую машину, и затем зарегистрировать его командой SBRteEnvironment.exe /regserver. После этих незамысловатых действий окружение готово к работе. Если планируется использовать окружение в клиентском коде – его потребуется установить на все клиентские машины.
Если потребуется удалить окружение – надо просто разрегистрировать его командой SBRteEnvironment.exe /unregserver, и удалить файл SBRteEnvironment.exe.

Интерфейс для программной работы

Имя регистрируемого COM-сервера - SBRteEnvironment.Environment. А это значит, что на ISBL мы его можем получать функцией CreateObject, следующим образом:

GlobalEnvironment = CreateObject('SBRteEnvironment.Environment')

Объект окружения имеет три метода:

  1. procedure SetVar(const Name: WideString; Value: OleVariant); - установить значение Value переменной окружения с именем Name. Если переменная с именем Name существует - ее значение будет перезаписано, если не существует - будет добавлена переменная с таким именем, и значение будет записано в нее.
  2. procedure PopVar(const Name: WideString); - удалить из окружения переменную с именем Name. Если переменной с таким именем в окружении не существует - ничего делаться не будет.
  3. function FindItem(const Name: WideString): OleVariant; - найти переменную с именем Name и получить ее значение. Если переменной с именем Name не существует - будет возвращено пустое значение (Null).

Пример использования

В качестве примера я продемонстрирую, как удержать в глобальном окружении объект Microsoft Excel. Excel запускается довольно долго, поэтому имеет смысл после первого запуска не дать ему выгрузиться, чтобы следующее обращение к нему сработало быстрее. Чтобы не давать ему выгрузиться – положим ссылку на него в глобальное окружение.
Для начала, создадим объект Excel, и добавим его в окружение:

ENV_VARIABLE_NAME = 'Excel'
GlobalEnvironment = CreateObject('SBRteEnvironment.Environment')
Excel = GlobalEnvironment.FindItem(ENV_VARIABLE_NAME)
if VarIsNull(Excel)
 Excel = CreateObject('Excel.Application')
 GlobalEnvironment.SetVar(ENV_VARIABLE_NAME; Excel)
 ShowMessage('Переменная была добавлена в окружение.')
else
 ShowMessage('Переменная уже есть в окружении.')
endif

Теперь можем в любом месте поработать с подготовленным заранее объектом:

ENV_VARIABLE_NAME = 'Excel'
GlobalEnvironment = CreateObject('SBRteEnvironment.Environment')
Excel = GlobalEnvironment.FindItem(ENV_VARIABLE_NAME)
if VarIsNull(Excel)
  Exit('Переменной нет в окружении. Необходимо ее добавить.')
endif
// Работаем с переменной Excel как обычно.

Если объект более нам не потребуется – удалим его из окружения:

ENV_VARIABLE_NAME = 'Excel'
GlobalEnvironment = CreateObject('SBRteEnvironment.Environment')
Excel = GlobalEnvironment.PopVar(ENV_VARIABLE_NAME)

Детали реализации

Глобальное окружение реализовано в виде одиночки - шаблон проектирования Singleton. Приятной особенностью такой реализации является то, что мы можем обращаться к переменным в нем не только из разных вычислений в одном процессе, но даже и из разных процессов, запущенных одновременно. Например, если пользователь запустит сценарий, который по умолчанию выполняется в отдельном процессе – ISBL-код сценария получит доступ к переменным основного процесса. А значит, легко можно передать нужные данные из главного процесса в процесс сценария.

Глобальное окружение реализовано таким образом, что, будучи запущенным один раз – при первом вызове CreateObject('SBRteEnvironment.Environment'), - оно будет оставаться запущенным до тех пор, пока не будут завершены все процессы SBRte.exe и SBSce.exe. После завершения последнего из указанных процессов – глобальное окружение выгрузится автоматически. Именно такая реализация позволяет легко передавать переменные из одних вычислений в другие, и при этом не заботиться о сроке жизни самого глобального окружения.

Безопасность

Как я описал в деталях реализации – один процесс может легко получить доступ к объектам, которые были сложены в глобальное окружение другим процессом. Разумеется, эту особенность злоумышленник может попытаться использовать, чтобы перехватить данные, сложенные в окружение. Об этом стоит помнить – и либо не складывать данные требующие защиты в окружение, либо каким-то образом защищать их там.

Как не выстрелить себе в ногу

При работе с глобальным окружением, рано или поздно возникает желание положить в него какие-то из объектов IS-Builder. В принципе, это можно делать, но тут может поджидать одна неочевидная на первый взгляд ловушка:

  • глобальное окружение остается в памяти до тех пор, пока запущены процессы SBRte или SBSce
  • процесс SBRte остается в памяти до тех пор, пока кто-то удерживает ссылки на созданные им объекты
  • в итоге, если положить ссылку на объект SBRte в глобальное окружение – возникнет ситуация взаимных ссылок, и система в целом перестанет выгружаться.

Выйти из этой ситуации достаточно просто – как только объект IS-Builder, который лежал в глобальном окружении, перестанет быть нужен, убирайте его из окружения с помощью PopVar.

Исходный код утилиты и ее дальнейшее развитие

С любезного разрешения руководства, я прикладываю к статье не только готовую утилиту SBRteEnvironment.zip (230.11 Кб), но и ее исходный код SBRteGlobalEnvironmentSource.zip (15,89 Кб). Не уверен, что у меня будет возможность заниматься развитием утилиты по пожеланиям, но думаю наличие исходного кода позволит доработать утилиту самостоятельно всем желающим. Надеюсь, что если кто-то будет ее дорабатывать - обязательно поделится с нашим сообществом. Утилита была разработана под Delphi 2007.

Заключение

Надеюсь, что эта реализация глобального окружения окажется полезной на ваших проектах. В статье я постарался по возможности полно осветить работу с окружением, но если будут появляться вопросы – спрашивайте в комментариях к статье, постараюсь ответить.

28
Авторизуйтесь, чтобы оценить материал.
Андрей Девятьяров

Степан, это очень интересно!

Пару вопросов:

1. Ограничений на использование в зависимости от версии билдера нет?

2. Если на одной машине запущены 2 разных версии DIRECTUM, можно передавать между их вычислениями данные?

Степан Мурашов

1. От версии зависимости быть не должно. Окружение само по себе не обращается к DIRECTUM - и поэтому никак не зависит от его версии.

2. Да, передача между разными версиями DIRECTUM должна работать. Так же должна работать и передача с не-ISBL-ным кодом - например с JavaScript в обложке папки, и т.п. Главное - чтобы на время передачи данных были запущены процессы SBRte/SBSce.

Игорь Прищепов

Ай, спасибо!

Заценю обязательно. Об опыте эксплуатации  - доложусь.

 

Андрей Тазетдинов

Мне кажется есть более простой и надежный путь - создайте функцию работы с XML и простую табличку из двух полей. В одном имя пользователя в другом XML с переменными среды. Поиск по XPath довольно прост и не понадобится установка на каждой машине, плюс всега сохраняются данные, даже для веба. )))
 

Степан Мурашов

Андрей, безусловно, можно реализовать такое окружение разными способами, и у них разных способов будут разные преимущества. В предложенном Вами варианте - доступ к окружению из веба действительно может оказаться удобной фишкой в некоторых сценариях работы.

Я видел разные реализации окружения, но у них у всех был один общий недостаток - туда нельзя сложить COM-объекты, а в некоторых сценариях требуются именно они. И одна из фишек предложенной мной реализации - возможность сохранить в окружении ссылки на COM-объекты. Я, правда, забыл явно указать этот момент в статье, спасибо что напомнили.

Например, можно сложить в окружение "тяжелые" объекты 1С, которые не хочется загружать-выгружать на каждый чих - именно для них искали глобальное окружение на форуме. Тут же и объекты самого DIRECTUM, а так же Word с Excel, которые я описал в примере. Я не знаю, как эти объекты можно сложить в xml, и думаю, что такой возможности в принципе нет.

Александр Тихонов

Обнаружил в системных функциях (группа "Прочие функции") функцию "Окружение". Исходя из справки, она реализует тот же функционал, и не требуется никакой регистрации SBRteEnvironment.exe.

Степан Мурашов

Александр, указанная Вами функция представляет лишь упрощенный доступ к окружению IObject.Environment текущего объекта, над которым выполняется вычисление. Окружение объекта является локальным для него, т.е. из "совсем другого" вычисления доступ к этому окружению получить не удастся.

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

 

Денис Архипов

В 5.2.1 работает? 

GlobalEnvironment = CreateObject('SBRteEnvironment.Environment')
GlobalEnvironment.SetVar("TestName"; "TestValue")
Test = GlobalEnvironment.PopVar("TestName")
showmessage(Test)

Вот такой код в 5.2.1 возвращает Empty.

Виктор Выгановский
Test = GlobalEnvironment.PopVar("TestName")
showmessage(Test)
Вот такой код в 5.2.1 возвращает Empty.

Test = GlobalEnvironment.PopVar("TestName")  же обнуляет переменные

Если объект более нам не потребуется – удалим его из окружения: ENV_VARIABLE_NAME = 'Excel' GlobalEnvironment = CreateObject('SBRteEnvironment.Environment') Excel = GlobalEnvironment.PopVar(ENV_VARIABLE_NAME)

 

Авторизуйтесь, чтобы написать комментарий