そこで、アンマネージアプリケーションで.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クラスを使用した経験は膨大です。
ソースとテストはここにあります 。