В последнее время 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(".", "root\cimv2");
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(".", "root\cimv2");
ServicesSet.Security_.Privileges.AddAsString("SeDebugPrivilege",True);
выборка = ServicesSet.ExecQuery(ТекстЗапроса);
х64 = Неопределено;
х86 = Неопределено;
Этох64 = ложь;
Для каждого прога Из выборка Цикл
Путь = прога.Properties_("InstallLocation").Value+"bin\comcntr.dll";
Этох64 = не СтрНайти(прога.Properties_("Name").Value,"(x86-64)") = 0;
Если Этох64 Тогда
х64 = путь;
иначе
х86 = путь;
КонецЕсли;
КонецЦикла;
Возврат ?(х64 = Неопределено,х86,х64);
КонецФункции // ПолучитьПутьКDll()
Получаем пути установки 1С и подбираем нужный. Функция работает медленно.