ネイティブVKを介した1Cでの.Netクラスのクロスプラットフォーム使用。 またはLinuxでCOMを置き換える

これは、記事「アンマネージコードからの.Netクラスのクロスプラットフォーム使用」の続きです。 または、LinuxのIDispatchカウンターパート



そこで、アンマネージアプリケーションで.Netクラスを使用する機会を得ました。 次に、 「ネイティブ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); } //----------------------------
      
      







これらは基本的な方法です。 これで、1Cからコード呼び出しにアクセスできます。



最初に、変数とヘルパー関数を宣言します。



  ,;  () //        .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(); 
      
      







そして今、1Cで.Netを使用するためのコードを作成できます。 そして、この刺激的な瞬間が来ました!



  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    
      
      







すぐに、何らかの理由で1CはInvariantCultureプロパティを設定したいのですが、誰もそれを求めません。

Sat.Appendメソッド(「新しい文字列」)を呼び出すと、 プロシージャとして、1Cはまだ関数として呼び出し、.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"); (.); (.());
      
      







さて、悲しいことに進みましょう。

前の記事では、速度テストがあり、その呼び出し時間は1秒あたり300,000を超える呼び出しでした。



1Cについても同様のテストを実行します。



  ()  ;  // ()  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 for .Net

00:00:01.20 1Cの場合



つまり、通話速度は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; } }
      
      







今、あなたは1Cで呼び出すことができます



 =("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); } }
      
      





1Cで。



  (, , ) //   . ("="+); ("="+); ("="+);   () //   . =("TestDllForCoreClr., TestDllForCoreClr, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"); =(.(.(),"   ")); =(.1C()); .1=.(); .Test(); 
      
      





そして、モジュール変数で設定することを忘れないでください

  ;
      
      







将来的には、1C用の.NET(C#)の類似物を追加します。 AddHandlerまたはHandlingExternal Eventsを介して1Cで.Netイベントを使用するためのラッパークラスの動的コンパイル



ここで、外部コンポーネントテクノロジーの1C実装の欠点について説明する価値があります。



1.メソッドFindMethod、FindProp、IsPropReadable、IsPropWritable、GetNParams、HasRetVal、GetParamDefValueは絶対に不要です。

メソッド以来

bool CallAsProc

bool CallAsFunc

bool SetPropValおよびbool GetPropValは成功時に戻り値を持ちます

エラー情報はAddErrorを介して返されます。

はい。インデックスの呼び出しは、IDiapatchからの時代錯誤であり、インターフェイスの説明がありました。

呼び出しの速度を上げるため。



2. SetPropValメソッドとGetPropValメソッドが戻るとき、例外は発生しません

3.何らかの理由で、コードでこれが必要でない場合、プロパティ設定が行われます。

4.メソッドは関数として呼び出され、メソッドはプロシージャとして呼び出されます。



5.主な理由の1つは、VCメソッドからVCのコピーを返して転送できないことです。



私は個人的に何の問題も見ていません。 このタイプの値を定義し、pInterfaceValフィールドにリンクを設定します。



参照カウントは1C側で発生します。 メソッド呼び出しの期間中のみ、1Cオブジェクトを含めて転送できます。



将来的には、1C 用の.NET(C#)の例に従って、1Cで.Netオブジェクトのイベントを使用する前に開発できます AddHandlerまたはHandlingExternal Eventsを介して1Cで.Netイベントを使用するためのラッパークラスの動的コンパイル



「1Cの.Net。非同期HTTP要求、複数のファイルを送信するマルチパート/フォームデータ、gzipを使用したトラフィック圧縮、deflate、サイトの便利な解析など」のような非同期呼び出しを使用します



一般に、.Net統合はMicrosoft Dynamics AX ClrObjectにあります。



クロスプラットフォームCore Clrを使用すると、1Cに統合できます。 これは、インポート代替としてのLinuxに特に当てはまります。

これまでのところ、Windows 7.10に取り組んでいます。 LinuxとIOSはまだありませんが、仮想マシンですぐに確認します。 .Net Core CLRはここからダウンロードできます

私が1Cを助けたいと思うこと。 1Cで.Netクラスを使用した経験は膨大です。



ソースとテストはここにあります



All Articles