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