В последнее время 1С стала очень часто выпускать новые релизы платформы, к новым функциям которой привязывают типовые конфигурации, например БП 3.0.
Большинство крупных организаций ведут учет своей деятельности в нескольких системах и чаще всего некоторые из этих систем просто невозможно запустить без серьезных доработак и рефакторинга.
В связи с этим на серверах начинают соседствовать по несколько различных релизов платформ. Это затрудняет поддержку систем и их взаимодействия.
Данный код призван упростить жизнь программистам, работающим в таких условиях.
&НаКлиенте
Процедура Команда1(Команда)
Путь = ПутьКDLL;
ЗавершатьПринудительно = ЗавершатьПриложениеЕслиОноАктивно;
рез = ЗарегистрироватьНужнуюКомпоненту(Путь,ЗавершатьПринудительно);
КонецПроцедуры
&НаКлиенте
Процедура Команда2(Команда)
ВызовССервера(ПутьКDLL,ЗавершатьПриложениеЕслиОноАктивно);
КонецПроцедуры
&НаСервере
Процедура ВызовССервера(ПутьКDLL,ЗавершатьПриложениеЕслиОноАктивно)
Путь = ПутьКDLL;
ЗавершатьПринудительно = ЗавершатьПриложениеЕслиОноАктивно;
рез = ЗарегистрироватьНужнуюКомпоненту(Путь,ЗавершатьПринудительно);
КонецПроцедуры
#Область Механизм_регистрации_COM_компоненты
//Возвращаем текущий путь, может пригодиться для восстановления после работы
&НаКлиентеНаСервереБезКонтекста
Функция ЗарегистрироватьНужнуюКомпоненту(Путь,МожноОтключатьПриложение = истина)
//Проверяем досутпность dll
файл = новый Файл(Путь);
Если не файл.Существует() тогда возврат новый структура("Путь,Результат",Путь,ложь); КонецЕсли;
ПутьВозврат = Путь;
//Инициализируем системный объект
Каталог = новый Comобъект ("COMAdmin.COMAdminCatalog");
//Делаем выборку всех приложений
Приложения = Каталог.GetCollection("Applications");//TopCollection Applications AppObject.Key
Приложения.Populate();
//Ищем по имени наше приложение
ключ = неопределено;
for each Приложение in Приложения цикл
Если врег(Приложение.Name) = ВРег("V83_COMConnector") тогда
ключ = Приложение;
прервать;
КонецЕсли;
КонецЦикла;
//Если не нашли то создаем приложение и импортируем в него компоненту
Если ключ = неопределено Тогда
СоздатьПриложение(Путь);
возврат новый структура("Путь,Результат",ПутьВозврат,истина);
КонецЕсли;
//Если приложение найдено, то получаем все компоненты в нем
компоненты = Приложения.GetCollection("Components", Приложение.Key);// PropertyInfo App.Value[prop.Name]
компоненты.Populate();
//Ищем нашу компоненту по имени
ключ = неопределено;
ид = 0;
Для каждого Компонент Из компоненты Цикл
Если Компонент.Name = "V83.COMConnector.1" Тогда
ключ = Компонент;
прервать;
КонецЕсли;
ид = ид + 1;
КонецЦикла;
//проверка можем ли мы отключать приложение, это нужно чтобы никого не рубануть случайно, если пофиг то рубим
если не МожноОтключатьПриложение тогда
//если нельзя то проверяем запущено ли приложение
свва = Приложения.GetCollection("PropertyInfo", Приложение.Key);
свва.Populate();
CLSID = Неопределено;
Для каждого св Из свва Цикл
Если св.Name = "ID" тогда
CLSID = Приложение.Value(св.Name);
КонецЕсли;
КонецЦикла;
Locator = новый Comобъект("WbemScripting.SWbemLocator");
ServicesSet = Locator.ConnectServer(".", "rootcimv2");
ServicesSet.Security_.Privileges.AddAsString("SeDebugPrivilege",True);
выборка = ServicesSet.ExecQuery("SELECT * FROM Win32_Process Where Name = ""dllhost.exe""
| and CommandLine Like ""%/Processid:" + CLSID + "%"" ");
для каждого стр из выборка цикл
возврат новый структура("Путь,Результат",ПутьВозврат,ложь);
конеццикла;
конецЕсли;
//Если не нашли то создаем компоненту
Если ключ = неопределено Тогда
//для начала остановим приложение
Каталог.ShutdownApplication(Приложение.Name);
ИмпортироватьКомпоненту(путь,Каталог);
возврат новый структура("Путь,Результат",ПутьВозврат,истина);
КонецЕсли;
//Если нашли то получаем все ее свойства
свва = Компоненты.GetCollection("PropertyInfo", Компонент.Key);
свва.Populate();
//Ищем путь к компоненте для сравнения
dll = Неопределено;
Для каждого св Из свва Цикл
Если св.Name = "DLL" тогда
dll = Компонент.Value(св.Name);
Прервать;
КонецЕсли;
КонецЦикла;
//Если пути разные то удаляем текущую и импортируем новую компоненту
Если не врег(dll) = врег(Путь) Тогда
ПутьВозврат = dll;
Каталог.ShutdownApplication(Приложение.Name);
Компоненты.remove(ид);
компоненты.SaveChanges();
компоненты.Populate();
ИмпортироватьКомпоненту(путь,Каталог);
возврат новый структура("Путь,Результат",ПутьВозврат,истина);
КонецЕсли;
возврат новый структура("Путь,Результат",ПутьВозврат,ложь);
КонецФункции
#Область Служебные_процедуры
&НаКлиентеНаСервереБезКонтекста
Процедура ИмпортироватьКомпоненту(знач Путь, Каталог)
попытка
Имя = "V83_COMConnector";
Каталог.InstallComponent(Имя,путь,"","");
исключение
//сообщить(ОписаниеОшибки());
конецпопытки;
КонецПроцедуры
&НаКлиентеНаСервереБезКонтекста
Процедура СоздатьПриложение(путь)
попытка
СкриптМенеджер = Новый COMОбъект("MSScriptControl.ScriptControl");
Скрипт = " Function SetFuncValue(Path)
|Dim objCatalog 'As COMAdminCatalog
|Set objCatalog = CreateObject(""COMAdmin.COMAdminCatalog"")
|Dim objApplicationsColl 'As COMAdminCatalogCollection
|Set objApplicationsColl = objCatalog.GetCollection(""Applications"")
|Dim objApp 'As COMAdminCatalogObject
|Set objApp = objApplicationsColl.Add
|objApp.Value(""Name"") = ""V83_COMConnector""
|objApp.Value(""Description"") = ""Компонента 1С""
|objApp.Value(""ApplicationAccessChecksEnabled"") = false
|objApplicationsColl.SaveChanges
|objApplicationsColl.Populate
|Set roles = objApplicationsColl.GetCollection(""Roles"", objApp.key)
|Set newRole = roles.Add
|newRole.Value(""Name"") = ""CreatorOwner""
|roles.SaveChanges
|objCatalog.InstallComponent objApp.key,Path,"""",""""
|End Function";
СкриптМенеджер.Language = "vbscript";
СкриптМенеджер.AddCode(Скрипт);
СкриптМенеджер.Run("SetFuncValue", путь);
Исключение
сообщить(ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
#КонецОбласти
#КонецОбласти
Для корректной работы данного кода у пользователя, от имени которого запущена 1С, должны быть соответствующие права. Своих пользователей мы добавили в группу DCOM в AD, этого было достаточно.
Код имеет достаточно комментариев, поэтому расписывать его работу нет необходимости.
UPD:
Небольшая функция для получения пути к целевой dll:
Функция ПолучитьПутьКDll(Версия_ = "") Экспорт
Locator = новый Comобъект("WbemScripting.SWbemLocator");
ТекстЗапроса = "select * from WIN32_Product where Name like ""%1C:Предприятие 8%"" and Version = """ + Версия_+"""";
ServicesSet = Locator.ConnectServer(".", "rootcimv2");
ServicesSet.Security_.Privileges.AddAsString("SeDebugPrivilege",True);
выборка = ServicesSet.ExecQuery(ТекстЗапроса);
х64 = Неопределено;
х86 = Неопределено;
Этох64 = ложь;
Для каждого прога Из выборка Цикл
Путь = прога.Properties_("InstallLocation").Value+"bincomcntr.dll";
Этох64 = не СтрНайти(прога.Properties_("Name").Value,"(x86-64)") = 0;
Если Этох64 Тогда
х64 = путь;
иначе
х86 = путь;
КонецЕсли;
КонецЦикла;
Возврат ?(х64 = Неопределено,х86,х64);
КонецФункции // ПолучитьПутьКDll()
Получаем пути установки 1С и подбираем нужный. Функция работает медленно.
Ну естессно, теперь, после шикарной статьи от tormozit’а, легко клепать такие публикации, ага-ага.
Хм… после какой статьи??? Этот код был написан пол года назад, руки не доходили статью набросать. Весь код написан с нуля!
(1) Yashazz, Вы про эту статьюСтатья ???
Объект MSScriptControl.ScriptControl к сожалению не доступен в 64-битном процессе. Поэтому приведенный код в случае отсутствия COM+ приложения без ошибок выполнится только
— на толстом клиенте
— на 32-битном сервере
— в файловой базе в любом контексте
(4) tormozit, Хм…. почему же у меня работает на 64-битном сервере и на тонком клиенте??? Да и клиентские системы 64-битные….
Этот код выполняется клиентами на клиентских машинах (32 и 64), он для этого и писался чтобы не бегать по клиентам и не ставить вручную.
Только что специально смоделировал (Тонкий клиент 64 система сервер 64) все работает.
Если у вас падает с ошибкой то ошибку в студию попробуем разобраться.
(5) Попробуйте выполнить код
на 64-битном сервере.
(6) tormozit, вы правы, действительно падает с ошибкой, код которым можно заменить вызов com объекта:
Показать
Как руки дойдут допишу статью.
(7) спасибо, помогло на Windows Server 2012 R2 x64!
(7) Процедура СоздатьПриложение(путь) создает OLE соединение V83.Application, или COM соединение V83.COMConnector (с ограниченными возможностями, запускается небольшая серверная часть)?
(9) V83.COMConnector
(10)
Создать OLE соединение V83.Application в консоли Службы компонентов в принципе создать невозможно ни программно, ни вручную?
И еще просьба уточнить: если один пользователь подключился к базе на одной платформе, а другому пользователю потребовалось подключился к другой базе на другой платформе, то его подключение невозможно не прервав подключение подключения первого пользователя?