gifts2017

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

Опубликовал Дмитрий Жиляков (Zhilyakovdr) в раздел Администрирование - Системное

Код позволяет перед подключением установить нужную версии 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(".", "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С и подбираем нужный. Функция работает медленно.

Скачать файлы

Наименование Файл Версия Размер Кол. Скачив.
Пример работы кода
.epf 8,42Kb
31.08.15
5
.epf 8,42Kb 5 Скачать
Пример работы кода (добавлен код для выполнения на сервере без com объекта)
.epf 8,67Kb
01.09.15
8
.epf 8,67Kb 8 Скачать

См. также

PowerTools от 1 000
Подписаться Добавить вознаграждение

Комментарии

1. Яков Коган (Yashazz) 31.08.15 22:19
Ну естессно, теперь, после шикарной статьи от tormozit'а, легко клепать такие публикации, ага-ага.
2. Дмитрий Жиляков (Zhilyakovdr) 31.08.15 23:00
Хм... после какой статьи??? Этот код был написан пол года назад, руки не доходили статью набросать. Весь код написан с нуля!
3. Дмитрий Жиляков (Zhilyakovdr) 31.08.15 23:04
4. Сергей Старых (tormozit) 01.09.15 08:35
Объект MSScriptControl.ScriptControl к сожалению не доступен в 64-битном процессе. Поэтому приведенный код в случае отсутствия COM+ приложения без ошибок выполнится только
- на толстом клиенте
- на 32-битном сервере
- в файловой базе в любом контексте
5. Дмитрий Жиляков (Zhilyakovdr) 01.09.15 09:10
(4) tormozit, Хм.... почему же у меня работает на 64-битном сервере и на тонком клиенте??? Да и клиентские системы 64-битные....
Этот код выполняется клиентами на клиентских машинах (32 и 64), он для этого и писался чтобы не бегать по клиентам и не ставить вручную.
Только что специально смоделировал (Тонкий клиент 64 система сервер 64) все работает.
Если у вас падает с ошибкой то ошибку в студию попробуем разобраться.
6. Сергей Старых (tormozit) 01.09.15 09:23
(5) Попробуйте выполнить код
Новый COMОбъект("MSScriptControl.ScriptControl")
на 64-битном сервере.
7. Дмитрий Жиляков (Zhilyakovdr) 01.09.15 11:44
(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 """+СтрокаВыполненияСкрипта;
        ЗапуститьПриложение(СтрокаКВыполнению);
...Показать Скрыть


Как руки дойдут допишу статью.
Для написания сообщения необходимо авторизоваться
Прикрепить файл
Дополнительные параметры ответа