Pull to refresh

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

Reading time14 min
Views8.7K
Это продолжение статьи «Кроссплатформенное использование классов .Net из неуправляемого кода. Или аналог IDispatch на Linux».

Там мы получили возможность использования классов .Net в неуправляемом приложении. Теперь воспользуемся «Создание компонент с использованием технологии Native API».

Итак, начнем.

Для начала определим используемые классы:

// Определение функции для единообразного вызова методов
	typedef bool(*CallAsFunc) (void * , tVariant* , tVariant* , const long);

	// Вспомогательный класс для хранения и единообразного вызова
	// Что бы не городить кучу switch 
	class MethodsArray
	{
	public:
		// Имя метода на кириллице
		//1С ники понимают только на нём
		wstring MethodName;
         // Ссылка на метод
		CallAsFunc  Method;
		//Количество параметров
		long    ParamCount;
		//Признак возвращаемого значения
		bool    HasRetValue;
       // Метод инициализации класса
		void Init(wstring MethodName, CallAsFunc  Method, long    ParamCount, bool    HasRetValue);
	};

	
	///////////////////////////////////////////////////////////////////////////////
	// class CAddInNative
	class BaseNetObjectToNative : public IComponentBase
	{
	public:
	
		static BaseNetObjectToNative* pCurrentObject;

		// Ссылка на массив методов
		MethodsArray* pMethodsArray;
		// Размер массива параметров
		int SizeArray;
		// Имя класса для 1С
		wstring ClassName;
		// Имя текущего вызываемого метода
		wstring MethodName;
		// Сслка на объект для вызова методв класса .Net
		ManagedDomainLoader* NetProvider;

		// Строковое представление объекта .Net для передачи в параметрах методов.
		wstring RefNetObject;
		// Индекс в массиве объектов на стороне .Net 
		long  IdNetObject;

		// Текущий найденный метод ВК
		MethodsArray* CurrentElem;
		// Нужен для нахождения количества параметрах м методах с переменных их количествои и перегрузках
		long LastParamsIndex;



class LoaderCLR :public  BaseNetObjectToNative
	{
	public:

		// Массив методов из двух элементов
		MethodsArray MethodsArray[2];
		LoaderCLR();
		virtual ~LoaderCLR();

		virtual bool ADDIN_API Init(void*  pConnection);
		virtual bool ADDIN_API setMemManager(void* memManager);

		// Метод для перичной инициализации .Net
		static bool CreateDamain(void* Self, tVariant* pvarRetValue, tVariant* paParams, const long lSizeArray);
		// Метод для предотвращения выгрузки DLL
		static bool LoadDLL(void* Self, tVariant* pvarRetValue, tVariant* paParams, const long lSizeArray);
		
       
		
       // Метод для выделения памяти ссылка на который отправляется в .Net
		static void* GetMem(long ByteCount);
		// Метод для сообщения об ссылка на который отправляется в .Net
		static void  AddError(const wchar_t* ErrorInfo);
	};

	class NetObjectToNative :public BaseNetObjectToNative
	{
	public:
		MethodsArray MethodsArray[3];
		NetObjectToNative();
	

		
		// Установка ссылки для передачи в параметрах и получение индекса объекта в сиске объектов .Net 
		static bool SetNetObjectRef(void* Self, tVariant* pvarRetValue, tVariant* paParams, const long lSizeArray);
		// Получение ссылки для передачи в параметрах
		static bool GetNetObjectRef(void* Self, tVariant* pvarRetValue, tVariant* paParams, const long lSizeArray);



	
	};


Теперь перейдем к реализации. Остановлюсь только на основных

	//---------------------------------------------------------------------------//
	long BaseNetObjectToNative::FindProp(const WCHAR_T* wsPropName)
	{
		// Свойства есть только у .Net классов.
		if (NetProvider == nullptr) return -1;
		long plPropNum = 1;

		// Устанавливаем MethodName для вызова GetPropVal или SetPropVal
		// и не ищем их на стороне .Net
		MethodName = wsPropName;

		return plPropNum;
	}

	//---------------------------------------------------------------------------//
	const WCHAR_T* BaseNetObjectToNative::GetPropName(long lPropNum, long lPropAlias)
	{

		// Можно вернуть MethodName но лень.
		return 0;
	}
	//---------------------------------------------------------------------------//
	bool BaseNetObjectToNative::GetPropVal(const long lPropNum, tVariant* pvarPropVal)
	{
		// Установим на всякий случай для выделения памяти текущий объект
		SetMemoryManager();
		// Свойства есть только у .Net классов. Их и вызываем используя соххраненное имя свойства 
		//MethodName
		return NetProvider->pGetPropVal(IdNetObject,MethodName.c_str(), pvarPropVal);

	}
	//---------------------------------------------------------------------------//
	bool BaseNetObjectToNative::SetPropVal(const long lPropNum, tVariant* varPropVal)
	{
		// Аналогично GetPropVal
		return NetProvider->pSetPropVal(IdNetObject, MethodName.c_str(), varPropVal);
	}
	//---------------------------------------------------------------------------//
	bool BaseNetObjectToNative::IsPropReadable(const long lPropNum)
	{
		// Не будем лезть в .Net. 
		// Подразумевается, что автор сам следит за тем, что делает
		// Если свойство нечитаемо будет выдана ошибка. Но к сожалению 1С эту ошибку не обрабатывает.
			return true;
		
	}
	//---------------------------------------------------------------------------//
	bool BaseNetObjectToNative::IsPropWritable(const long lPropNum)
	{
		// Аналогично IsPropReadable
			return true;
		
	}
	//---------------------------------------------------------------------------//
	long BaseNetObjectToNative::GetNMethods()
	{
		// Не знаем сколько методов
		return 0;
	}
	//---------------------------------------------------------------------------//
	long BaseNetObjectToNative::FindMethod(const WCHAR_T* wsMethodName)
	{
	// Запомним имя метода
		MethodName = wsMethodName;
   // Сначала посмотрим есть ли метод в компоненте
		long res= findMethod(MethodName);
		if (res==0 && NetProvider == nullptr) return -1;

		// Так как методы .Net используют перегрузку и используя params 
		//можно указать параметр метода, принимающий переменное количество аргументов.
		//LastParamsIndex нужен для нахождения количества использумых параметров в вызываемом методе
		LastParamsIndex = -1;
		MethodName = wsMethodName;
		return res;
	
	}
	//---------------------------------------------------------------------------//
	const WCHAR_T* BaseNetObjectToNative::GetMethodName(const long lMethodNum,
		const long lMethodAlias)
	{

		return 0;//MethodName.c_str();
	}
	//---------------------------------------------------------------------------//
	long BaseNetObjectToNative::GetNParams(const long lMethodNum)
	{
		// Здесь  возвращаем количество параметров
		//Для парамс ограничеваем 16 параметрами
		if (lMethodNum==0)
			return  NetProvider->pGetNParams(IdNetObject, MethodName.c_str());
		else
			return CurrentElem->ParamCount;
		
	
	}
	//---------------------------------------------------------------------------//
	bool BaseNetObjectToNative::GetParamDefValue(const long lMethodNum, const long lParamNum,
		tVariant *pvarParamDefValue)
	{

		// В этом методе идет запрос значения по умолчанию
		//Для нас это означает испльзуемое количество параметров в вызываемом методе
		if (LastParamsIndex == -1) LastParamsIndex = lParamNum;

		pvarParamDefValue->vt = VTYPE_I4;
		pvarParamDefValue->lVal = 0;






		return true;
	}
	//---------------------------------------------------------------------------//
	bool BaseNetObjectToNative::HasRetVal(const long lMethodNum)
	{
		if (lMethodNum > 0) return CurrentElem->HasRetValue;
		// Для .Net классов считаем что все возвращают значения. Даже если нет, то вернем null
		return true;
	}
	//---------------------------------------------------------------------------//
	bool BaseNetObjectToNative::CallAsProc(const long lMethodNum,
		tVariant* paParams, const long lSizeArray)
	{
		
		// Вызываем метод. Но 1С вызывает его не по тому как он вызван, а зависит от HasRetVal
		if (lMethodNum==0)
		{
			SetMemoryManager();
			if (LastParamsIndex == -1) LastParamsIndex = lSizeArray;
			return NetProvider->pCallAsFunc(IdNetObject, MethodName.c_str(), 0, paParams, LastParamsIndex);
		}
		
		return CurrentElem->Method(this, 0, paParams, lSizeArray);

		
	}
	//---------------------------------------------------------------------------//
	bool BaseNetObjectToNative::CallAsFunc(const long lMethodNum,
		tVariant* pvarRetValue, tVariant* paParams, const long lSizeArray)
	{
		// Вызываем метод. Но 1С вызывает его не по тому как он вызван, а зависит от HasRetVal
		// Даже если он вызван как процедура 1С вызывает его как функцию.
		pvarRetValue->vt = VTYPE_NULL;
		pvarRetValue->lVal = 0;

		if (lMethodNum == 0)
		{
			SetMemoryManager();
			if (LastParamsIndex == -1) LastParamsIndex = lSizeArray;
			return NetProvider->pCallAsFunc(IdNetObject, MethodName.c_str(), pvarRetValue, paParams, LastParamsIndex);
		}

		return CurrentElem->Method(this, pvarRetValue, paParams, lSizeArray);
	}
	//----------------------------




Это основные методы. Теперь можно перейти к вызовам кода из 1С.

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

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

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

Функция Ъ(Ссылка)
	
	// Зосдадим объект ВК
    рез = Новый("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(ИмяФайла);
КонецПроцедуры



А теперь потирая руки можно создать код для использования .Net в 1С. И вот этот волнительный момент настал!

Процедура ТестStringBuilderНажатие(Элемент)
	СБ=ъ(Врап.Новый("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 осуществляется внутри ВК
КонецПроцедуры



Сразу отмечу, то что 1С зачем то хочет установить свойство InvariantCulture хотя её об этом никто не просит.
Если я вызову метод Сб.Append(«Новая Строка»); как процедуру, то 1С все равно вызывает как функцию и на стороне .Net сохраняется в списке, из которого нужно освобождать.

Теперь посмотрим более сложный пример.

// Создадим 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;
 Сообщить(СтрДлина(стр));



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

Можно загружать сторонние библиотеки.

Тестовый=ъТип("TestDllForCoreClr.Тестовый, TestDllForCoreClr, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
Тест=ъ(Врап.Новый(Тестовый.ПолучитьСсылку()," Свойство из Конструктора"));
Сообщить(Тестовый.Поле);
Тестовый.Поле="Установка из 1С";
Сообщить(Тестовый.Поле);

Сообщить(Тест.СвойствоОбъекта);

Тест.СвойствоОбъекта=("Установлено Свойство из 1С");
Сообщить(Тест.СвойствоОбъекта);
Сообщить(Тест.ПолучитьСтроку());


Теперь перейдем к более грустному.
В предыдущей статье был тест скорости время вызова кторого составляло оболее 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:06.45 Для .Net
00:00:01.20 Для 1С

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

Добавил поддержку объектов с поддержкой IDynamicMetaObjectProvider

Для теста создад ExpandoObject

  public object ПолучитьExpandoObject()
        {

            dynamic res = new ExpandoObject();
            res.Имя = "Тест ExpandoObject";
            res.Число = 456;
            res.ВСтроку = (Func<string>)(() => res.Имя);
            res.Сумма = (Func<int, int, int>)((x, y) => x + y);

            return res;
        }



И наследника DynamicObject

class TestDynamicObject : DynamicObject
    {

        public override bool TrySetMember(SetMemberBinder binder, object value)
        {

            return true;
        }
        // получение свойства
        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            result = binder.Name;
            return true;
        }
        // вызов метода
        public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
        {
            var res = new StringBuilder("{0}(");
            var param = new object[args.Length + 1];
            param[0] = binder.Name;
            if (args.Length > 0)
            {
                Array.Copy(args, 0, param, 1, args.Length);
                for (int i = 0; i < args.Length; i++)
                {
                    res.AppendFormat("{{{0}}},", i + 1);

                }

                res.Remove(res.Length - 1, 1);

            }
            res.Append(")");

            result = String.Format(res.ToString(), param);
            return true;


        }
    }



Теперь можно вызвать на 1С

Тестовый=ъТип("TestDllForCoreClr.Тестовый, TestDllForCoreClr, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
	Тест=ъ(Врап.Новый(Тестовый.ПолучитьСсылку()," Свойство из Конструктора"));
	
	// Это аналог структуры, но с поддержкой методов
	РасширяемыйОбъект=ъ(Тест.ПолучитьExpandoObject());
	
	Сообщить("ВСтроку="+РасширяемыйОбъект.ВСтроку());
	Сообщить("Сумма=" + РасширяемыйОбъект.Сумма(1, 2));
	
	Сообщить(РасширяемыйОбъект.Имя);
	Сообщить(РасширяемыйОбъект.Число);
	
	РасширяемыйОбъект.Имя="Новое Имя";
	РасширяемыйОбъект.Число=768;
	// Добавим новое свойство
	РасширяемыйОбъект.НовоеСвойство="Новое Свойство";
	
	Сообщить(РасширяемыйОбъект.Имя);
	Сообщить(РасширяемыйОбъект.Число);
	Сообщить(РасширяемыйОбъект.НовоеСвойство);
	
	НовыйРеквизит=ъ(Врап.Новый("System.Dynamic.ExpandoObject, System.Dynamic.Runtime, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"));
	
	НовыйРеквизит.Имя="Новый Реквизит";
	НовыйРеквизит.Значение=123;
	
	
	РасширяемыйОбъект.НовыйРквизит=НовыйРеквизит.ПолучитьСсылку();
	Сообщить(ъ(РасширяемыйОбъект.НовыйРквизит).Имя);
	
	
	TestDynamicObject=ъТип("TestDllForCoreClr.TestDynamicObject, TestDllForCoreClr, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
	
	ДинамикОбъект=ъНовый(TestDynamicObject.ПолучитьСсылку());
	ДинамикОбъект.УстановимЛюбоеСвойство="Чего то там";
	Сообщить( ДинамикОбъект.ПолучитТоЧтоПередали);
	Сообщить(ДинамикОбъект.ПолучитТоЧтоПередалиСПараметрами(1,3.45,ТекущаяДата()));




Это удобно при работе с различными парсерами

Добавил вывод типов для дженерик методов
Теперь можно не выводить отдельно метод


// public T ДженерикМетод<V, T>(V param1, T param2, V param3) 
Сообщить(Тест.ДженерикМетод(1,"Привет",3));
//


//public V ДженерикМетод2<K, V>(Dictionary<K, V> param1, K param2, V param3)

Словарь= ъНовый("System.Collections.Generic.Dictionary`2[System.Int32,System.String]");
Сообщить(Тест.ДженерикМетод2(Словарь.ПолучитьСсылку(),3,"Привет2"));

// public K ДженерикМетод3<K>(IList<K> param1, int param2, K param3)

List=ъНовый("System.Collections.Generic.List`1[System.String]");
Сообщить(Тест.ДженерикМетод3(List.ПолучитьСсылку(),3,"Привет3"));



Сделал поддержку внешнего события
В классе создадим пле типа Action<string, string, string>

 public class Тестовый 
    { 
      public Action<string, string, string>   ВнешнееСобытие1С;

// И сделаем эмуляцию события.



public async void TestВнешнегоСобытия() 
        { 
            for(int i=0;i<100; i++)
            {
                var значение = i.ToString();
                Task.Run(async() =>
                {
                   await Task.Delay(1000).ConfigureAwait(false);
                   ВнешнееСобытие1С?.DynamicInvoke("Тестовый", "ТестовоеСообщение", значение);
                });

                await Task.Delay(50).ConfigureAwait(false);
            }


        } 


В 1С.

Процедура ВнешнееСобытие(Источник, Событие, Данные)
    // Вставить содержимое обработчика.

    
    Сообщить("Источник="+Источник);
    Сообщить("Событие="+Событие);
    Сообщить("Данные="+Данные);
КонецПроцедуры

Процедура ТестВнешнегоСобытияНажатие(Элемент)
    // Вставить содержимое обработчика.

    Тестовый=ъТип("TestDllForCoreClr.Тестовый, TestDllForCoreClr, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");

Тест=ъ(Врап.Новый(Тестовый.ПолучитьСсылку()," Свойство из Конструктора")); 

Делегат=Ъ(Врап.ПолучитьДелегатВнешнегоСобытия1C());
Тест.ВнешнееСобытие1С=Делегат.ПолучитьСсылку();
Тест.TestВнешнегоСобытия();
КонецПроцедуры



И не забыть в Переменных модуля установить
Перем   Тест;


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

Теперь стоит поговорить о недостатках 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С.

Исходники и тесты можно посмотреть здесь.
Tags:
Hubs:
Total votes 15: ↑7 and ↓8-1
Comments12

Articles