Решаем проблему с разными версиями платформ при COM соединении


Код позволяет перед подключением установить нужную версии comcntr.dll

В последнее время 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С и подбираем нужный. Функция работает медленно.

12 Comments

  1. Yashazz

    Ну естессно, теперь, после шикарной статьи от tormozit’а, легко клепать такие публикации, ага-ага.

    Reply
  2. Zhilyakovdr

    Хм… после какой статьи??? Этот код был написан пол года назад, руки не доходили статью набросать. Весь код написан с нуля!

    Reply
  3. Zhilyakovdr

    (1) Yashazz, Вы про эту статью Статья ???

    Reply
  4. tormozit

    Объект MSScriptControl.ScriptControl к сожалению не доступен в 64-битном процессе. Поэтому приведенный код в случае отсутствия COM+ приложения без ошибок выполнится только

    — на толстом клиенте

    — на 32-битном сервере

    — в файловой базе в любом контексте

    Reply
  5. Zhilyakovdr

    (4) tormozit, Хм…. почему же у меня работает на 64-битном сервере и на тонком клиенте??? Да и клиентские системы 64-битные….

    Этот код выполняется клиентами на клиентских машинах (32 и 64), он для этого и писался чтобы не бегать по клиентам и не ставить вручную.

    Только что специально смоделировал (Тонкий клиент 64 система сервер 64) все работает.

    Если у вас падает с ошибкой то ошибку в студию попробуем разобраться.

    Reply
  6. tormozit

    (5) Попробуйте выполнить код

    Новый COMОбъект(«MSScriptControl.ScriptControl»)

    на 64-битном сервере.

    Reply
  7. Zhilyakovdr

    (6) tormozit, вы правы, действительно падает с ошибкой, код которым можно заменить вызов com объекта:

    Скрипт = «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,»»»+путь+»»»,»»»»,»»»»
    |»;
    ВрСкрипт = ПолучитьИмяВременногоФайла(«vbs»);
    
    ЗТ = Новый ЗаписьТекста(ВрСкрипт, КодировкаТекста.ANSI);
    ЗТ.Закрыть();
    ЗТ = Новый ЗаписьТекста(ВрСкрипт,,, Истина, Символы.ПС);
    ЗТ.Записать(Скрипт);
    ЗТ.Закрыть();
    
    СтрокаВыполненияСкрипта = «wscript.exe «+ВрСкрипт+»»»»;
    СтрокаКВыполнению  = «cmd /C «»»+СтрокаВыполненияСкрипта;
    ЗапуститьПриложение(СтрокаКВыполнению);

    Показать

    Как руки дойдут допишу статью.

    Reply
  8. Terve!R

    (7) спасибо, помогло на Windows Server 2012 R2 x64!

    Reply
  9. M_Volkov

    (7) Процедура СоздатьПриложение(путь) создает OLE соединение V83.Application, или COM соединение V83.COMConnector (с ограниченными возможностями, запускается небольшая серверная часть)?

    Reply
  10. Zhilyakovdr

    (9) V83.COMConnector

    Reply
  11. M_Volkov

    (10)

    V83.COMConnector

    Создать OLE соединение V83.Application в консоли Службы компонентов в принципе создать невозможно ни программно, ни вручную?

    Reply
  12. M_Volkov

    И еще просьба уточнить: если один пользователь подключился к базе на одной платформе, а другому пользователю потребовалось подключился к другой базе на другой платформе, то его подключение невозможно не прервав подключение подключения первого пользователя?

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *