Каталог решений - Кроссплатформенное использование классов .Net в 1С через Native ВК. Или замена COM на Linux

Кроссплатформенное использование классов .Net в 1С через Native ВК. Или замена COM на Linux

Кроссплатформенное использование классов .Net в 1С через Native ВК. Или замена COM на Linux

В наличии

С выходом .Net Core CLR стало возможным использование классов .Net не только на Windows, но и других операционных системах.
Это особенно важно в разрезе импортозамещения. И главное — это аналог COM под Linux.
Сейчас под Linux для расширения возможностей 1С используются Web сервисы. Данная разработка позволит использовать не только сборки входящие в состав .Net Core, но и пользовательские портативные сборки.

Категория:

Описание

Являясь автором Использование сборок .NET в 1С 7.x b 8.x. Создание внешних Компонент, я часто слышал в свой адрес, что это все туфта, ибо не кроссплатформенна.  Кроме того, под Windows полно готовых COM библиотек и несложно сделать свои COM библиотеки на разных языках. Для Linux ситуация сложнее. Там нет COM, а технология .Native API не слишком-то и дружелюбна.

Поэтому было решено, используя Native API, сделать ВК, которая будет взаимодействовать с .Net библиотекой для использования классов .Net в 1С. Следует учесть, что .Net Core CLR пока только на начальном пути, и многое недоступно, что есть в большом .Net, но даже того, что есть, достаточно для расширения возможностей 1С.

Одним из недостатком Native API является то, что мы не можем возвратить ВК и передать параметры ВК в метод ВК. Поэтому пришлось возвращать ссылку на объект в виде строки. И создавать методы для обертки ВК над строкой.

Не буду описывать внутренности ВК. Описание и исходники можно посмотреть 

Кроссплатформенное использование классов .Net из неуправляемого кода. Или аналог IDispatch на Linux

Кроссплатформенное использование классов .Net в 1С через Native ВК. Или замена COM на Linux

 

Там же можно скачать и исходники.  

Сначала определим вспомогательные методы для создания объектоа типов и обертки.

 

Перем   Врап,СсылкаНаДомен;

Функция Ъ(Ссылка)
	
	// Создаем объект по ссылке полученной из методов .Net классов
	//Физически это строка ёЁ<Ьъ>№_%)Э?&2 содержащее 12 символов для отделения их от других строк
	//и индекс в спике исполуемых объектов на стороне .Net

	рез = Новый("AddIn.NetObjectToNative.NetObjectToNative");
	// И установим ссылку
	рез.УстановитьСсылку(Ссылка);    
	возврат  рез
КонецФункции // СоздатьОбъектПоСсылке()

// Сокращенное использование метода ВК Новый
// Создает объект по строковому представлению типа или по ссылке на тип
Функция ъНовый(стр)
	возврат ъ(Врап.Новый(стр));
КонецФункции

// Сокращенное использование метода ВК ПолучитьТип
// Создает получает тип по строковому представлению типа 
Функция ъТип(стр)
	возврат ъ(Врап.ПолучитьТип(стр));
КонецФункции



Процедура ПриОткрытии() 
	
	// Установим отчет рядом с  AddInNetObjectToNative.dll
	// NetObjectToNative.dll
	// и библиотеками  Microsoft.NETCore.App\1.0.0\	
	// Так как нужны 32 разрядные так как клиент 1С 32 разрядный
	// Скачать можно здесь https://github.com/dotnet/cli
	// На сервере можно использовать 64 разрядную Core Clr
	
	Файл=Новый Файл(ЭтотОбъект.ИспользуемоеИмяФайла);  
	КаталогОтчета=Файл.Путь;	
	
	ИмяФайла=КаталогОтчета+"\AddInNetObjectToNative.dll";
	
	ПодключитьВнешнююКомпоненту(ИмяФайла, "NetObjectToNative",ТипВнешнейКомпоненты.Native); 
	Врап = Новый("AddIn.NetObjectToNative.LoaderCLR");
	CoreClrDir=КаталогОтчета+"\bin\";
	ДиректорияNetObjectToNative=КаталогОтчета;
	
	СсылкаНаДомен=Врап.СоздатьОбертку(CoreClrDir,ДиректорияNetObjectToNative,"");
	Врап.ЗагрузитьDLL(ИмяФайла);
	
	
КонецПроцедуры

 Я создал каталог, в который положил библиотеки

AddInNetObjectToNative.dll это сама ВК

NetObjectToNative.dll это .Net библиотека для получения классов .Net из .Net

TestDllForCoreClr.dll это тестовая сборка для загрузки её в пространство .Net и использования класса и его методов и свойств.

Кроме того, в папке Bin\ лежит сам .Net Core Clr, который можно скачать отсюда 

.NET Core Installers and Binaries

Ну вот он, долгожданный миг, когда можно наконец-то использовать классы .Net Core CLR.

Заодно проверим передаваемые типы.

СБ=ъ(Врап.Новый("System.Text.StringBuilder","Первая Строка"));
	CultureInfo=ъТип("System.Globalization.CultureInfo");
	
	CultureInfoES=ъ(Врап.Новый(CultureInfo.ПолучитьСсылку(),"es-ES"));
	
	
	Сообщить(СБ.Capacity);
	Сообщить(СБ.ПолучитьСсылку());
	
	InvariantCulture=ъ(CultureInfo.InvariantCulture);
	
	// К сожалению 1С вызывает метод имеющий возвращаемое значение как функцию даже если вызов идет как процедура
	//Нужно очистить ссылку в списке объектов
	ссылка=Сб.Append("Новая Строка"); Врап.ОчиститьСсылку(ссылка);
	ссылка=Сб.AppendLine();   Врап.ОчиститьСсылку(ссылка);
	ссылка=Сб.Append("Вторая Строка"); Врап.ОчиститьСсылку(ссылка);
	ссылка=Сб.AppendLine();     Врап.ОчиститьСсылку(ссылка);
	
	ссылка=Сб.AppendFormat("AppendFormat {0}, {1}, {2}, {3}, {4},", "Строка", 21, 45.89, ТекущаяДата(),истина );   Врап.ОчиститьСсылку(ссылка);
	ссылка=Сб.AppendLine(); Врап.ОчиститьСсылку(ссылка);
	
	// Так как в параметрах можно передавать только простые типы закодирум ссылку на объект в строку
	ссылка=Сб.AppendFormat(CultureInfoES.ПолучитьСсылку(),"AppendFormat {0}, {1}, {2}, {3}, {4},", "Строка", 21, 45.89, ТекущаяДата(),истина );  Врап.ОчиститьСсылку(ссылка);
	ссылка=Сб.AppendLine(); Врап.ОчиститьСсылку(ссылка);
	
	ссылка=Сб.AppendFormat(InvariantCulture.ПолучитьСсылку(),"AppendFormat {0}, {1}, {2}, {3}, {4},", "Строка", 21, 45.89, ТекущаяДата(),истина ); 
	
	Сообщить(СБ.ToString());
	Сообщить("Ёмкостъ ="+СБ.Capacity);
	// Увеличим емкость
	СБ.Capacity=СБ.Capacity+40;
	Сообщить("Новая Ёмкостъ ="+СБ.Capacity);
	
	// Очистка ссылок СБ и  СultureInfo осуществляется внутри ВК

Теперь перейдем к более сложным классам. А именно HTTPClient с возможностью сжатия трафика


 
// Вставить содержимое обработчика.
	//	System.Net.Http, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
	
	
	// Создадим HttpClient и вызовем Get запрос используя сжатие трафика 
	// На данный момент я не нашел как получить загруженные сборки или как загрузить сборку по имени файла
	// Поэтому загружаем по полному имени
	HttpClient=ъТип("System.Net.Http.HttpClient, System.Net.Http, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
	HttpClientHandler = ъТип("System.Net.Http.HttpClientHandler, System.Net.Http, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
	DecompressionMethods= ъТип("System.Net.DecompressionMethods, System.Net.Primitives, Version=4.0.11.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
	
	
	handler = ъНовый(HttpClientHandler.ПолучитьСсылку());
	// Можно использовать и так. Только Не соберем ссылки
	//handler.AutomaticDecompression=Врап.OR(DecompressionMethods.GZip,DecompressionMethods.Deflate);
	
	ссылкаGZip=DecompressionMethods.GZip;
	ссылкаDeflate=DecompressionMethods.Deflate;
	
	// В 1С нет бинарных операция. Для этого на стороне .Net есть функция
	handler.AutomaticDecompression=Врап.OR(ссылкаGZip,ссылкаDeflate);
	Врап.ОчиститьСсылку(ссылкаGZip);   Врап.ОчиститьСсылку(ссылкаDeflate);
	
	Клиент = ъ(Врап.Новый(HttpClient.ПолучитьСсылку(),handler.ПолучитьСсылку()));
	
	uriSources ="https://msdn.microsoft.com/en-us/library/system.net.decompressionmethods(v=vs.110).aspx";
	
	Стр=ъ(Клиент.GetStringAsync(uriSources)).Result;
	Сообщить(СтрДлина(стр));

Ух ты, и это работает!

Еще одна особенность Native API в том, что мы можем получить двоичные данные, но вот передать их в параметры метода метода ВК нет.

Дальше идет проверка вызова сторонней библиотеки и работа с двоичными данными.


 
Тестовый=ъТип("TestDllForCoreClr.Тестовый, TestDllForCoreClr, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
	Тест=ъ(Врап.Новый(Тестовый.ПолучитьСсылку()," Свойство из Конструктора"));
	Сообщить(Тестовый.Поле);
	Тестовый.Поле="Установка из 1С";
	Сообщить(Тестовый.Поле);
	
	Сообщить(Тест.СвойствоОбъекта);
	
	Тест.СвойствоОбъекта=("Установлено Свойство из 1С");
	Сообщить(Тест.СвойствоОбъекта);
	Сообщить(Тест.ПолучитьСтроку());
	
	
	Врап.ДвоичныеДанныеКакОбъект=истина;
	ДД=Тест.ПолучитьДвоичныеДанные();
	UnicodeEncoding= ъНовый("System.Text.UnicodeEncoding, System.Text.Encoding.Extensions, Version=4.0.10.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
	//Если не установить Врап.ДвоичныеДанныеКакОбъект=истина;
	//То при вызове  UnicodeEncoding.GetString(ДД);
	// 1С выдает ошибку
	// Неверный аргумент
	
	СтрокаИзДД=UnicodeEncoding.GetString(ДД);
	Сообщить(СтрокаИзДД);

 

Самое главное, что мы можем использовать сторонние библиотеки, которые должны лежать рядом с NetObjectToNative.dll

К сожалению, в отличие от COM в .Native ВК мы не можем использовать энумераторы

 Для Каждого Элемент ИЗ Списка Цикл

 

Поэтому для упрощения использования массивов и списков создана функция ПолучитьЭнумератор


 
Функция ПолучитьЭнумератор(Объект)
	
	Перечислимый=ъ(Врап.ПолучитьИнтерфейс(Объект.ПолучитьСсылку(),"IEnumerable"));
	ПеречислительСсылка=Перечислимый.GetEnumerator();
	// На всякий случай приведем к Интерфейсу IEnumerator
	Перечислитель=ъ(Врап.ПолучитьИнтерфейс(ПеречислительСсылка,"IEnumerator"));
	Врап.ОчиститьСсылку(ПеречислительСсылка);	
	// Можно и так
	//IEnumerable=ъТип("System.Collections.IEnumerable");
	//IEnumerator=ъТип("System.Collections.IEnumerator")
	// Перечислимый=ъ(Врап.ПолучитьИнтерфейс(Объект.ПолучитьСсылку(),IEnumerable));
	//ПеречислительСсылка=Перечислимый.GetEnumerator();
	// На всякий случай приведем к Интерфейсу IEnumerator
	// Перечислитель=ъ(Врап.ПолучитьИнтерфейс(ПеречислительСсылка,IEnumerator));
	//Врап.ОчиститьСсылку(ПеречислительСсылка);	
	
	возврат  Перечислитель
КонецФункции // ПолучитьЭнумератор()

Процедура ТестЭнумератораНажатие(Элемент)
	List=ъТип("System.Collections.Generic.List`1[System.String]");
	
	Список=ъНовый(List.ПолучитьСсылку());
	Для сч=1 По 10 Цикл
		Список.Add(строка(сч));    
	КонецЦикла; 
	
	// Получим энумератор через соответствующие интерфейсы
	// Заодно проверим  метод ПолучитьИнтерфейс
	СписокЭнум=ПолучитьЭнумератор(Список);
	
	Пока СписокЭнум.MoveNext() Цикл
		//  Врап.ВСтроку вывоит строковое представление всех типов в том числе числовые, строки, неопределено
		Сообщить(СписокЭнум.Current);
		
	КонецЦикла; 
	
	// Получение Энумератора из ВК
	СписокЭнум2=ъ(Врап.ПолучитьЭнумератор(Список.ПолучитьСсылку()));
	
	Пока СписокЭнум2.MoveNext() Цикл
		//  Врап.ВСтроку вывоит строковое представление всех типов в том числе числовые, строки, неопределено
		Сообщить(СписокЭнум2.Current);
		
	КонецЦикла; 
	
КонецПроцедуры

Теперь перейдем к более грустному.
В этой статье был тест скорости, время вызова которого составляло более 300 000 вызовов в секунду.

Проведем аналогичный тест на 1С:


 
Функция ПолучитьЧисло(зн)
	возврат зн;	
КонецФункции // ()

Процедура ТестСкорости2Нажатие(Элемент)
	// Вставить содержимое обработчика.
	КоличествоИтераций=200000;
	Тестовый=ъТип("TestDllForCoreClr.Тестовый, TestDllForCoreClr, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
	Тест=ъ(Врап.Новый(Тестовый.ПолучитьСсылку()," Свойство из Конструктора"));
	
	stopWatch = ъНовый("System.Diagnostics.Stopwatch,System.Runtime.Extensions, Version=4.0.11.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
	
	стр="";
	Тест.ПолучитьЧисло(1);
	
	НачВремя=ТекущаяДата();
	stopWatch.Start();
	
	
	Для сч=1 по КоличествоИтераций Цикл
		Тест.ПолучитьЧисло(сч);
		
	КонецЦикла;
	
	stopWatch.Stop();
	
	ВремяВыполнения=ТекущаяДата()-НачВремя;
	Сообщить("ПолучитьЧисло ="+ВремяВыполнения);
	ВывестиВремя(stopWatch);
	
	
	НачВремя=ТекущаяДата();
	stopWatch.Restart();
	
	
	Для сч=1 по КоличествоИтераций Цикл
		ПолучитьЧисло(сч);
		
	КонецЦикла;
	
	stopWatch.Stop();
	
	ВремяВыполнения=ТекущаяДата()-НачВремя;
	Сообщить("ПолучитьЧисло ="+ВремяВыполнения);
	ВывестиВремя(stopWatch);
	
КонецПроцедуры

 

00:00:09.63  Для .Net
00:00:01.23  Для 1С

 

00:00:09.93 Для .Net
00:00:01.20 Для 1С

То есть скорость вызова уменьшилась до  20 000 вызовов в секунду.
Но и этого достаточно, так обычно вызываются более тяжелые методы.

Добавил поддержку IDynamicMetaObjectProvider (DynamicObject,ExpandoObject).  В исходниках есть примеры. Это важно при использовании различного рода парсеров

Теперь стоит поговорить о недостатках 1С реализации Технологии Внешних Компонент.

1. Абсолютно не нужны методы FindMethod, FindProp, IsPropReadable, IsPropWritable, GetNParams, HasRetVal, GetParamDefValue

Так как у методов

bool CallAsProc

bool CallAsFunc

bool SetPropVal и bool GetPropVal есть возвращаемое значение об успешном выполнении

Информация об ошибке возвращается через AddError.

Да и вызов по индексу это анахронизм от IDiapatch, где было описание диспинтерфейсов для увеличения скорости вызова.

2. При возвращении методами SetPropVal и GetPropVal исключение не вызывается

3. Зачем-то происходит установка свойств там, где в коде этого не требуется.

4. Вызывается метод как функция там, где метод вызывается как процедура.

5. Один из основных — это нельзя вернуть и передать экземпляр ВК из методов ВК.

Я лично не вижу никаких проблем. Определить значение для такого типа и установить ссылку в поле pInterfaceVal.

Подсчет ссылок происходит на стороне 1С. Передавать можно в том числе и объекты 1С только на время вызова метода.

В дальнейшем можно развить до использования событий объектов .Net в 1С по примеру .NET(C#) для 1С. Динамическая компиляция класса обертки для использования .Net событий в 1С через ДобавитьОбработчик или ОбработкаВнешнегоСобытия

Использовать асинхронные вызовы по примеру «.Net в 1С. Асинхронные HTTP запросы, отправка Post нескольких файлов multipart/form-data, сжатие трафика с использованием gzip, deflate, удобный парсинг сайтов и т.д.»

Вообще интеграция .Net есть в Microsoft Dynamics AX ClrObject.

Используя кроссплатформенный Core Clr, можно интегрировать в 1С. Особенно это актуально для Linux как импортозамещения.

Пока проверил, работает на Windows 7,10. Linux и IOS пока нет, но в скором проверю на виртуальной машине. .Net Core CLR можно скачать здесь

С чем я бы с удовольствием помог 1С. Есть огромный опыт использования классов .Net в 1С.

has been added to your cart:
Оформление заказа