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

в 12:22, , рубрики: .net, , 1С-Битрикс, C#, c++, Core Clr

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

Использование сборок .Net через обертку реализующую IReflect
Для подключения .NET сборок используется «CLR Hosting API»
1C.Net: Предприятие – пример коммерческого успеха .Net-решений в России
Как вызвать метод из C# в 1С?

Но все они используют в той или иной степени COM. С появлением .Net Core стало возможным использование сборок .Net и на любой оси отличной от Windows.

На просторах интернета было найдено решение: Hosting .NET Core Clr in your own process и simpleCoreCLRHost.

Суть подключения заключается в загрузке библиотеки coreclr.dll, получения нужных интерфейсов и запуск CLR Runtime Host.

Теперь мы можем получить ссылки на статические методы из класса .Net:

// Метод для установки ссылок на нативные методы для выделения памяти и сообщении об ошибки
public static void SetDelegate(IntPtr ДляВыделенияПамяти,IntPtr ДляВызоваОшибки)

// Метод для вызова функции или процедуры (для процедуры ReturnValue==null)
public static bool CallAsFunc(int Target, IntPtr ИмяМетодаPtr, IntPtr ReturnValue, IntPtr МассивПараметров, int РазмерМассива)

// Этот метод нужен для ВК из 1С
public static   int GetNParams(int Target, IntPtr ИмяМетодаPtr)

// Методы для установки и получения свойства
public static  bool SetPropVal(int Target, IntPtr ИмяСвойстваPtr, IntPtr pvarPropVal)

public static bool GetPropVal(int Target, IntPtr ИмяСвойстваPtr, IntPtr varPropVal)

// Удаление объекта из списка используемых объектов
public static void DeleteObject(int Target)

Теперь можно получить ссылки на них из C++. Объявим типы методов:

typedef bool(STDMETHODCALLTYPE *ManagedCallAsFunc)(const __int32, const wchar_t*, tVariant* pvarRetValue, tVariant* paParams, const __int32  lSizeArray);
	typedef int(STDMETHODCALLTYPE *ManagedGetNParams)(const __int32, const wchar_t*);
	typedef bool(STDMETHODCALLTYPE *ManagedGetPropVal)(const __int32, const wchar_t*, tVariant*);
	typedef bool(STDMETHODCALLTYPE *ManagedSetPropVal)(const __int32, const wchar_t*, tVariant*);
	typedef void(STDMETHODCALLTYPE *ManagedSetDelegate)(void*(*) (long), void(*) (const wchar_t*));
	typedef void(STDMETHODCALLTYPE *ManagedDeleteObject)(const __int32);

Теперь получим ссылки на них через функцию:

bool ManagedDomainLoader::CreateDelegate(DWORD appDomainID,  wstring MethodName, INT_PTR * fnPtr)
	{

		HRESULT	hr = ClrLoader::pClrLoader->pCLRRuntimeHost->CreateDelegate(
			appDomainID,
			L"NetObjectToNative", // Имя Сборки
			L"NetObjectToNative.AutoWrap", // Имя класса
			MethodName.c_str(), //Имя импортируемого метода
			(INT_PTR*)fnPtr); // Ссылка на импортируемый метод

		if (FAILED(hr))
		{
			wprintf_s(L"Failed to create a delegate to the managed entry point: %sn", MethodName.c_str());
			printf_s("Failed to create a delegate to the managed entry point: (%d).n", hr);
			ClrLoader::pClrLoader->pCLRRuntimeHost->UnloadAppDomain(appDomainID, true);
			return false;
		}
	
		
		return true;
	}

И соответственно вызов:

if (!CreateDelegate(domainId,L"CallAsFunc", (INT_PTR*)&pCallAsFunc)) return false;
		if (!CreateDelegate(domainId, L"GetNParams", (INT_PTR*)&pGetNParams)) return false;
		if (!CreateDelegate(domainId, L"GetPropVal", (INT_PTR*)&pGetPropVal)) return false;
		if (!CreateDelegate(domainId, L"SetPropVal", (INT_PTR*)&pSetPropVal)) return false;
		if (!CreateDelegate(domainId, L"DeleteObject", (INT_PTR*)&pDeleteObject)) return false;
		if (!CreateDelegate(domainId, L"SetDelegate", (INT_PTR*)&pSetDelegate)) return false;

// Передадим ссылки на нужные методы
		pSetDelegate(ManagedDomainLoader::GetMem, ManagedDomainLoader::AddError);

Первая часть марлезонского балета благополучно закончилась. Теперь нужно решить несколько задач:

1. Хранилище для объектов .Net. Мы не можем передать ссылку на объект .Net так как объекты .Net подвергаются дефрагментации. Плюс нужно держать ссылку на них, для предотваращения их от сборки мусора;
2. Сделать оболочку для вызова методов и свойств;
3. Сделать систему поиска и вызова методов классов. Дело в том, что в Core .Net такой мощной функции Type.InvokeMember;
4. Сделать аналог структуры Variant в COM и получение и установка значений в него.

Но зато, решив эту задачу, мы сможем использовать методы расширения и дженерик методы, которые определяются по типам параметров. Итак, начнем с хранилища. Это реализация списка на массиве. Элементарный аналог менеджера памяти. Основная задача: установить объект, получить и удалить.

 public struct ЭлементХранилища
    {
        internal AutoWrap Объект;
        internal int Next;


       internal ЭлементХранилища(AutoWrap Объект)
        {

            this.Объект = Объект;
            Next = -1;
        }

        internal ЭлементХранилища(AutoWrap Объект, int next)
        {

            this.Объект = Объект;
            Next = next;
        }
    }

   internal class ХранилищеОбъектов
    {
     
         List<ЭлементХранилища> Элементы= new List<ЭлементХранилища>();
         int FirstDeleted = -1;

        public int Add(AutoWrap Объект)
        {

            var элемент = new ЭлементХранилища(Объект);

// Если нет удаленных записей, то добавляем и возвращаем индекс на последний элемент
            if (FirstDeleted == -1)
            { Элементы.Add(элемент);
                return Элементы.Count-1;
            }
            else
            {
            // Если есть удаленные то берем первый из списка
           // и корректируем ссылку на начало цепочки свободных индексов
               int newPos = FirstDeleted;
                FirstDeleted = Элементы[newPos].Next;
                Элементы[newPos] = элемент;
                return newPos;

            }
 
        }

        public void RemoveKey(int Pos)
        {
            if (Pos > 0 && Pos < Элементы.Count && Элементы[Pos].Объект != null)
            {
               
                var Элемент = new ЭлементХранилища(null, FirstDeleted);
                Элементы[Pos] =Элемент;
            
                FirstDeleted = Pos;
            }
        }

        public AutoWrap GetValue(int Pos)
        {
            if (!(Pos > -1 && Pos < Элементы.Count && Элементы[Pos].Объект != null))
                return null;
            return Элементы[Pos].Объект;

        }

    }

Теперь перейдем к созданию обертки.


 public class AutoWrap
    {

        
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        internal delegate IntPtr ВыделитьПамятьDelegate(int КоличествоБайтов);

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        internal delegate void ИнформацияОбОшибкеDelegate(IntPtr Ошибка);

        // Хранилище где будем хранить объекты
        internal static ХранилищеОбъектов СписокОбъектов;
        // Нужен для 1С, так какона понимает тоолько примитивные типы. Byte[] может только принимать
        //Поэтому будем передавать индекс в хранилище через строку и начало её зокодирум 
        internal static string ХэшДляСсылки = "ёЁ<Ьъ>№_%)Э?&";//new Guid().GetHashCode();
            

        // ссылка на объект
        protected internal object O = null;
        // Ссылка на тип. Нужна для скорости и для использования типа интерфейса
        protected internal Type T = null;

        protected internal int ИндекасВСписке;

        // Для типа можно вызвать статические методы
        internal bool ЭтоТип;
        // Для перечислений нужно вызывать Enum.Parse(T, name);
        internal bool IsEnum;

        // ЭтоExpandoObject тоже отдельно обрабатывается.
        //В дальнейшем реализую поддерку DynamicObject или универсально DynamicMetaObject
        internal bool ЭтоExpandoObject;

        // Это анахронизмы от COM
        internal static bool ЭтоСемерка = false;
        internal static bool ВыводитьСообщениеОбОшибке = true;
        internal static Exception ПоследняяОшибка = null;

        // Делегат для выделения памяти на стороне неуправляемого кода
        internal static ВыделитьПамятьDelegate ВыделитьПямять;
        //Делегат для сообщения об ошибке в неуправляемый код
        internal static ИнформацияОбОшибкеDelegate ИнформацияОбОшибке;

        //Вызвается из натива. Устанавливаем нужные делегаты
        public static void SetDelegate(IntPtr ДляВыделенияПамяти,IntPtr ДляВызоваОшибки)
        {
            ВыделитьПямять = Marshal.GetDelegateForFunctionPointer<ВыделитьПамятьDelegate>(ДляВыделенияПамяти);
            ИнформацияОбОшибке = Marshal.GetDelegateForFunctionPointer<ИнформацияОбОшибкеDelegate>(ДляВызоваОшибки);

        }
        static AutoWrap()
        {
            // В начале установим ссылку на вспомогательный класс
            //Для создания объектов, получения типов итд
            //который будет идти в списке под индексом 0
            СписокОбъектов = new ХранилищеОбъектов();
            var первый = new AutoWrap(typeof(NetObjectToNative));

        }


        public AutoWrap(object obj)
        {

            ИндекасВСписке = СписокОбъектов.Add(this);
            O = obj;
            if (O is Type)
            {
                T = O as Type;
                ЭтоТип = true;
            }
            else
            {
                T = O.GetType();
                ЭтоТип = false;
                ЭтоExpandoObject = O is System.Dynamic.ExpandoObject;
                IsEnum = T.GetTypeInfo().IsEnum;


            }



        }

        // Нужен для установки типа интерфейса
        public AutoWrap(object obj, Type type)
        {
            ИндекасВСписке = СписокОбъектов.Add(this);
            O = obj;
            T = type;
            ЭтоТип = false;
            //   ЭтоExpandoObject = O is System.Dynamic.ExpandoObject;

        }

Теперь опишем вариант. Вернее, он описан 1С:

//    struct _tVariant
    //    {
    //        _ANONYMOUS_UNION union
    //        {
    //            int8_t i8Val;
    //            int16_t shortVal;
    //            int32_t lVal;
    //        int intVal;
    //        unsigned int uintVal;
    //        int64_t llVal;
    //        uint8_t ui8Val;
    //        uint16_t ushortVal;
    //        uint32_t ulVal;
    //        uint64_t ullVal;
    //        int32_t errCode;
    //        long hRes;
    //        float fltVal;
    //        double dblVal;
    //        bool bVal;
    //        char chVal;
    //        wchar_t wchVal;
    //        DATE date;
    //        IID IDVal;
    //        struct _tVariant *pvarVal;
    //        struct tm      tmVal;
    //        _ANONYMOUS_STRUCT struct
    //        {
    //            void* pInterfaceVal;
    //        IID InterfaceID;
    //    }
    //    __VARIANT_NAME_2/*iface*/;
    //        _ANONYMOUS_STRUCT struct
    //        {
    //            char* pstrVal;
    //    uint32_t strLen; //count of bytes
    //}
    //__VARIANT_NAME_3/*str*/;
    //        _ANONYMOUS_STRUCT struct
    //        {
    //            WCHAR_T* pwstrVal;
    //uint32_t wstrLen; //count of symbol
    //        } __VARIANT_NAME_4/*wstr*/;
    //    } __VARIANT_NAME_1;
    //    uint32_t cbElements;    //Dimension for an one-dimensional array in pvarVal
    //TYPEVAR vt;
    //};
    public enum EnumVar
    {
        VTYPE_EMPTY = 0,
        VTYPE_NULL,
        VTYPE_I2,                   //int16_t
        VTYPE_I4,                   //int32_t
        VTYPE_R4,                   //float
        VTYPE_R8,                   //double
        VTYPE_DATE,                 //DATE (double)
        VTYPE_TM,                   //struct tm
        VTYPE_PSTR,                 //struct str    string
        VTYPE_INTERFACE,            //struct iface
        VTYPE_ERROR,                //int32_t errCode
        VTYPE_BOOL,                 //bool
        VTYPE_VARIANT,              //struct _tVariant *
        VTYPE_I1,                   //int8_t
        VTYPE_UI1,                  //uint8_t
        VTYPE_UI2,                  //uint16_t
        VTYPE_UI4,                  //uint32_t
        VTYPE_I8,                   //int64_t
        VTYPE_UI8,                  //uint64_t
        VTYPE_INT,                  //int   Depends on architecture
        VTYPE_UINT,                 //unsigned int  Depends on architecture
        VTYPE_HRESULT,              //long hRes
        VTYPE_PWSTR,                //struct wstr
        VTYPE_BLOB,                 //means in struct str binary data contain
        VTYPE_CLSID,                //UUID
        VTYPE_STR_BLOB = 0xfff,
        VTYPE_VECTOR = 0x1000,
        VTYPE_ARRAY = 0x2000,
        VTYPE_BYREF = 0x4000,    //Only with struct _tVariant *
        VTYPE_RESERVED = 0x8000,
        VTYPE_ILLEGAL = 0xffff,
        VTYPE_ILLEGALMASKED = 0xfff,
        VTYPE_TYPEMASK = 0xfff,
        VTYPE_AutoWrap = 0xff // Нужен для внутреннего использования
            // Хотя может использоваться отдельно от 1С

    };

    public class РаботаСВариантами
    {
        internal static Dictionary<Type, EnumVar> СоответствиеТипов;
        
        static РаботаСВариантами()
        {

            СоответствиеТипов = new Dictionary<Type, EnumVar>()
            { 
                { typeof(Int16),EnumVar.VTYPE_I2 },
                {typeof(Int32),EnumVar.VTYPE_I4 },
                {typeof(float),EnumVar.VTYPE_R4 },
                {typeof(double),EnumVar.VTYPE_R8 },
                {typeof(bool),EnumVar.VTYPE_BOOL },
                {typeof(sbyte),EnumVar.VTYPE_I1 },
                {typeof(byte),EnumVar.VTYPE_UI1 },
                {typeof(UInt16),EnumVar.VTYPE_UI2},
                {typeof(UInt32),EnumVar.VTYPE_UI4},
                {typeof(Int64),EnumVar.VTYPE_I8},
                {typeof(UInt64),EnumVar.VTYPE_UI8},
                {typeof(string),EnumVar.VTYPE_PWSTR},
                {typeof(byte[]),EnumVar.VTYPE_BLOB},
                {typeof(DateTime),EnumVar.VTYPE_DATE},
                {typeof(AutoWrap),EnumVar.VTYPE_AutoWrap},
            };


        }

        public static DateTime ConvertTmToDateTime(IntPtr Элемент)
        {

                tm val = Marshal.PtrToStructure<tm>(Элемент);
                return val.ToDateTime();
     


    }
      public static  object ПолучитьОбъекИзIntPtr(IntPtr Элемент)
        {
            IntPtr текПоз = Элемент + 44;
            int размерIntPtr = Marshal.SizeOf<IntPtr>();
            EnumVar тип =(EnumVar) Marshal.ReadInt16(текПоз);

            switch (тип)
            {
                case EnumVar.VTYPE_EMPTY:
                case EnumVar.VTYPE_NULL: return null;
                case EnumVar.VTYPE_I2: return Marshal.ReadInt16(Элемент);
                case EnumVar.VTYPE_I4: return Marshal.ReadInt32(Элемент);
                case EnumVar.VTYPE_R4: return Marshal.PtrToStructure<float>(Элемент);
                case EnumVar.VTYPE_R8: return Marshal.PtrToStructure<double>(Элемент);
                case EnumVar.VTYPE_BOOL:return Marshal.ReadByte(Элемент)!=0;
                case EnumVar.VTYPE_I1: return (sbyte)Marshal.ReadByte(Элемент);
                case EnumVar.VTYPE_UI1: return Marshal.ReadByte(Элемент);
                case EnumVar.VTYPE_UI2: return (UInt16)Marshal.ReadInt16(Элемент);

                case EnumVar.VTYPE_UI4: return (UInt32)Marshal.ReadInt32(Элемент);

                case EnumVar.VTYPE_I8: return Marshal.ReadInt64(Элемент);
                case EnumVar.VTYPE_UI8: return (UInt64)Marshal.ReadInt64(Элемент);
                case EnumVar.VTYPE_PWSTR:

                    var str= Marshal.PtrToStringUni(Marshal.ReadIntPtr(Элемент));

                    return AutoWrap.ПолучитьОбъектПоСсылке(str);

                case EnumVar.VTYPE_BLOB:
                    текПоз = Элемент + размерIntPtr;
                    byte[] res = new byte[Marshal.ReadInt32(текПоз)];
                    Marshal.Copy(Marshal.ReadIntPtr(Элемент), res,0,res.Length);
                    
                    return res;
                case EnumVar.VTYPE_DATE:
                    var date= Marshal.PtrToStructure<double>(Элемент);
                    return DateTimeHelper.FromOADate(date);

                
                case EnumVar.VTYPE_TM: return ConvertTmToDateTime(Элемент);
            }
            return null;
            }


        public static IntPtr ЗаписатьСтрокувIntPtr(string str)
        {

            var res = UnicodeEncoding.Unicode.GetBytes(str);

            IntPtr ВыделеннаяПамять = AutoWrap.ВыделитьПямять(res.Length + 2);
            Marshal.Copy(res, 0, ВыделеннаяПамять, res.Length);
            Marshal.WriteInt16(ВыделеннаяПамять + res.Length, 0);

            return ВыделеннаяПамять;
        }
        static void УстановитьСтрокуВIntPtr(string str, IntPtr Элемент)
        {

           

            Marshal.WriteIntPtr(Элемент, ЗаписатьСтрокувIntPtr(str));

            IntPtr текПоз = Элемент + Marshal.SizeOf<IntPtr>();
            Marshal.WriteInt32(текПоз, str.Length);

        }


       static  void УстановитьМассивБайтВIntPtr(byte[] value, IntPtr Элемент)
        {

    
            IntPtr ВыделеннаяПамять = AutoWrap.ВыделитьПямять(value.Length);
            Marshal.Copy(value, 0, ВыделеннаяПамять, value.Length);

            Marshal.WriteIntPtr(Элемент, ВыделеннаяПамять);

            IntPtr текПоз = Элемент + Marshal.SizeOf<IntPtr>();
            Marshal.WriteInt32(текПоз, value.Length);

        }
        public static bool УстановитьОбъектВIntPtr(object Объект, IntPtr Элемент)
        {

            IntPtr текПоз = Элемент + 44;
            int размерIntPtr = Marshal.SizeOf<IntPtr>();

            if (Объект == null)
            {

                Marshal.WriteInt16(текПоз, (Int16)EnumVar.VTYPE_NULL);
                Marshal.WriteInt32(Элемент, 0);
                return true;

            }


            EnumVar тип;

            var res = СоответствиеТипов.TryGetValue(Объект.GetType(), out тип);

            if (!res) return false;


            Marshal.WriteInt16(текПоз, (Int16)тип);
            switch (тип)
            {
                case EnumVar.VTYPE_I2:  Marshal.WriteInt16(Элемент,(Int16) Объект); break;
                case EnumVar.VTYPE_I4:  Marshal.WriteInt32(Элемент, (Int32)Объект); break;
                case EnumVar.VTYPE_R4:
                    double val = (double)(float)Объект;
                    Marshal.StructureToPtr<double>(val, Элемент,false);
                    Marshal.WriteInt16(текПоз, (Int16)EnumVar.VTYPE_R8);
                    break;
                case EnumVar.VTYPE_R8:  Marshal.StructureToPtr<double>((double)Объект, Элемент, false); break;
                case EnumVar.VTYPE_BOOL:  Marshal.WriteByte(Элемент, Convert.ToByte(Объект)); break;
                case EnumVar.VTYPE_I1:  Marshal.WriteByte(Элемент, Convert.ToByte(Объект)); break;
                case EnumVar.VTYPE_UI1:  Marshal.WriteByte(Элемент, (byte)Объект); break;
                case EnumVar.VTYPE_UI2:  Marshal.WriteInt16(Элемент, Convert.ToInt16(Объект)); break;

                case EnumVar.VTYPE_UI4:  Marshal.WriteInt32(Элемент, Convert.ToInt32(Объект)); break;

                case EnumVar.VTYPE_I8:  Marshal.WriteInt64(Элемент, (Int64)Объект); break;
                case EnumVar.VTYPE_UI8: Marshal.WriteInt64(Элемент, Convert.ToInt64(Объект)); break;
                case EnumVar.VTYPE_PWSTR: УстановитьСтрокуВIntPtr((string)Объект, Элемент); break;

                case EnumVar.VTYPE_BLOB:  УстановитьМассивБайтВIntPtr((byte[])Объект, Элемент); break;
                case EnumVar.VTYPE_DATE:  Marshal.StructureToPtr<double>(((DateTime)Объект).ToOADate(), Элемент, false);  break;
                case EnumVar.VTYPE_AutoWrap:
                    УстановитьСтрокуВIntPtr(((AutoWrap)Объект).ПолучитьСсылку(), Элемент);
                    Marshal.WriteInt16(текПоз, (Int16)EnumVar.VTYPE_PWSTR);
                    break;
                    
            }
            return true;
        }

    }

Остальнуюю реализацию можно посмотреть в исходниках или посмотреть здесь Вызов управляемого кода (.Net Core) из неуправляемого

Перейду к использованию на С++:

void TestCallMethod(NetObjectToNative::ManagedDomainLoader* mD, long Target)
{
	tVariant Params[4];
	tVariant RetVal;
	tVariant* paParams = Params;
	typedef std::chrono::high_resolution_clock Clock;
	auto start = Clock::now();
	

	long r = 0;
	for (int i = 0; i < 1000000; i++)
	{
		
		paParams->lVal = i;
		paParams->vt = VTYPE_I4;
		mD->pCallAsFunc(Target, L"ПолучитьЧисло", &RetVal, paParams, 1);
		
		r += RetVal.lVal;
		r %= 1 << 30;

	}

	auto finish = Clock::now();


	auto elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(finish - start).count() / 1000000000.0;


	wprintf_s(L"Tme=:  %.2f  secondn", elapsed);
	wprintf_s(L"Eval Value=:  %d n", r);
}

long GetTarget(tVariant* CurParam)
{
	wchar_t* curstr = CurParam->pwstrVal;
	curstr += 13;
	wstring temp = curstr;
	return stol(temp);

}


int main()
{
	setlocale(0, "");

	// Загрузим Core CLR
	// И создадим домен
	//Первый параметр это путь к папке с coreclr.dll
	NetObjectToNative::ManagedDomainLoader* mD = NetObjectToNative::ManagedDomainLoader::InitManagedDomain(L"c:\Program Files\DNX\runtimes\dnx-coreclr-win-x86.1.0.0-rc1-update1\bin\", L"", L"");

	if (!mD)  return 0;

	tVariant Params[4];
	tVariant RetVal;
	tVariant* paParams = Params;

	paParams->vt = VTYPE_PWSTR;
	paParams->pwstrVal = L"System.Text.StringBuilder";

	cout << "Press Key";
	cin.get();

	// 0 это индекс вспомогательного класса для получения типов объектов и прочих удобный методов
	bool res = mD->pCallAsFunc(0, L"Новый", &RetVal, paParams, 1);
	if (!res) return 0;


	// Так как в 1С нет возможности установить пользовательский тип
	// То возвращаем строку первый 12 символов постоянное значени. После них long в строку
	// третьи это индекс в списке экспортируемых объектов
	// Можно его передавать в качестве параметра типа BLOB
	
	// Теперь мы можем передавать ссылку ref в параметрах;
	
	long Target = GetTarget(&RetVal); // Получили индекс в списке

	wprintf_s(L"index : %dn", Target);

	// Память выделяется на стороне натива. Нам и удалять.
	delete[] RetVal.pstrVal;

	paParams->vt = VTYPE_PWSTR;
	paParams->pwstrVal = L"Новая Строка";

	// 0 так как вызывается как void даже если метод что то и возвращает, что бы не оборачивать результат
	res = mD->pCallAsFunc(Target, L"Append", 0, paParams, 1);

	res = mD->pCallAsFunc(Target, L"ToString", &RetVal, paParams, 0);
	wprintf_s(L"ToString() : %sn", RetVal.pwstrVal);

	delete[] RetVal.pstrVal;

	paParams->vt = VTYPE_I4;
	paParams->lVal = 40;
	res = mD->pSetPropVal(Target, L"Capacity", paParams);


	res = mD->pGetPropVal(Target, L"Capacity", &RetVal);
	wprintf_s(L"Capacity : %dn", RetVal.lVal);

	// Вызовим ошибочный метод
	if (!mD->pGetPropVal(Target, L"Несуществующее Свойство", &RetVal)) 
		wprintf_s(L"Ошибка при получении свойства : %sn", L"Несуществующее Свойство");

	// Удалим объект из списка. Теперь его может собрать GC
	mD->pDeleteObject(Target);

	// Создадим объект через тип

	paParams->vt = VTYPE_PWSTR;
	paParams->pwstrVal = L"System.Text.StringBuilder";
	// Получим ID оббъекта typeof(System.Text.StringBuilder)
	res = mD->pCallAsFunc(0, L"ПолучитьТип", &RetVal, paParams, 1);

	//paParams[0] = RetVal;

	// Скопируем ID оббъекта typeof(System.Text.StringBuilder)
	
	//в RetVal у нас ссылка на typeof(System.Text.StringBuilder)

	//Просто скопируем параметры
	 paParams[0] = RetVal;
     
	 wchar_t* ref = RetVal.pwstrVal;
	// И создадим экземпляр StringBuilder
	res = mD->pCallAsFunc(0, L"Новый", &RetVal, paParams, 1);
	// Удалим строку
	delete[] ref;
	if (!res) return 0;
	
	// Удалим из списка по ссылке
	paParams[0] = RetVal;
	res = mD->pCallAsFunc(0, L"ОчиститьСсылку", &RetVal, paParams, 1);

	//Теперь Создадим эклемпляр класса из сторонней сборки
	paParams->vt = VTYPE_PWSTR;
	paParams->pwstrVal = L"TestDllForCoreClr.Тестовый, TestDllForCoreClr, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null";

	auto par2 = paParams;
	par2++;
	par2->vt = VTYPE_PWSTR;
	par2->pwstrVal = L"Свойство из Конструктора";
	res = mD->pCallAsFunc(0, L"Новый", &RetVal, paParams, 2);

	long testRef = GetTarget(&RetVal);
	paParams->vt = VTYPE_I4;
	paParams->lVal = 3;

	res = mD->pCallAsFunc(testRef, L"ПолучитьЧисло", &RetVal, paParams, 1);
	wprintf_s(L"input int : %dn", RetVal.lVal);


	TestCallMethod(mD, testRef);
	TestCallMethod(mD, testRef);
	TestCallMethod(mD, testRef);

В общем, это примитивный аналог IDispatch без поддержки подсчета ссылок. Но можно такой аналог сделать на стороне натива и использовать в .Net через обертку через DynamicObject. Скорость выполнения на миллион вызовов 2,7 секунды на моем i3-2120 3.3 GHz.

Если на Windows для 1С можно было решать через COM, то на линкус эта лавочка прикрылась. Но можно использовать Core .Net!

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

Исходники можно скачать Здесь

В следующей статье напишу об использовании .Net Core в 1С по технологии Native ВК

Автор: Serginio1

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js