Это продолжение статьи «Кроссплатформенное использование классов .Net из неуправляемого кода. Или аналог IDispatch на Linux».
Там мы получили возможность использования классов .Net в неуправляемом приложении. Теперь воспользуемся «Создание компонент с использованием технологии Native API».
Итак, начнем.
Для начала определим используемые классы:
Теперь перейдем к реализации. Остановлюсь только на основных
Это основные методы. Теперь можно перейти к вызовам кода из 1С.
Сначала объявим переменные и вспомогательные функции.
А теперь потирая руки можно создать код для использования .Net в 1С. И вот этот волнительный момент настал!
Сразу отмечу, то что 1С зачем то хочет установить свойство InvariantCulture хотя её об этом никто не просит.
Если я вызову метод Сб.Append(«Новая Строка»); как процедуру, то 1С все равно вызывает как функцию и на стороне .Net сохраняется в списке, из которого нужно освобождать.
Теперь посмотрим более сложный пример.
Ух ты, и это работает!
Можно загружать сторонние библиотеки.
Теперь перейдем к более грустному.
В предыдущей статье был тест скорости время вызова кторого составляло оболее 300 000 вызовов в секунду.
Проведем аналогичный тест на 1С:
00:00:06.45 Для .Net
00:00:01.20 Для 1С
То есть скорость вызова уменьшилась до 30 000 вызовов в секунду.
Но и этого достаточно, так обычно вызываются более тяжелые методы.
Добавил поддержку объектов с поддержкой IDynamicMetaObjectProvider
Для теста создад ExpandoObject
И наследника DynamicObject
Теперь можно вызвать на 1С
Это удобно при работе с различными парсерами
Добавил вывод типов для дженерик методов
Теперь можно не выводить отдельно метод
Сделал поддержку внешнего события
В классе создадим пле типа Action<string, string, string>
В 1С.
И не забыть в Переменных модуля установить
В будущем добавлю аналог .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С.
Исходники и тесты можно посмотреть здесь.
Там мы получили возможность использования классов .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С.
Исходники и тесты можно посмотреть здесь.
