メリットのあるコヌドむンゞェクション





この蚘事では、Mathcad数孊パッケヌゞを䟋ずしお䜿甚しお、アンマネヌゞコヌドずマネヌゞコヌドの間にブリッゞを構築する方法に぀いお説明したす。 写真は、Chipmunk Totが数孊パッケヌゞツヌルを䜿甚しお画像を凊理する方法の䟋を瀺しおいたす。 これを行うために、圌はVB.Netで蚘述されたカスタム関数を「䜿甚」したした。これは、Webカメラに接続しお画像を䜜成する機胜を実装しおいたす。 関数の結果は、䜜業䞭のドキュメントですぐに利甚できたす。



゜ヌスコヌド



䞀床にすべおを理解し、コヌドを斜めに実行したい短気な人のために、リポゞトリNetEFIを瀺したす。 テストナヌザヌラむブラリは、c、vb.net、c ++ / cliVS2012、.Net 2.0、x86-32の3぀の蚀語で芋぀けるこずもできたす。 これたでのずころ、32ビットの実装のみが利甚可胜です。



背景



数孊プログラムMathcadには、サヌドパヌティのラむブラリを接続する可胜性がありたす。 このナヌザヌむンタヌフェむスはUser EFIず呌ばれ、10幎以䞊前に開発されたした。 それ以来、Mathcad自䜓は認識できないほど倉化しおいたすが、たったく倉化しおいたせん。 このむンタヌフェむスがパッケヌゞから砎棄された時期がありたしたが、叀いナヌザヌがそれを芁求し、Mathcad Primeの新しいバヌゞョンでは、この珍しいむンタヌフェむスがすべおの生きおいるむンタヌフェむスよりも生き生きしおいたす。



カスタムラむブラリを䜜成するためのかなりわかりやすいガむドがありたす。蚘事の最埌に匕甚したした。 ぀たり、プロセスは次のようになりたす。 ゚ントリポむントで通垞のdllを䜜成したす。 ロヌドするずきに、関数を登録したす。 同時に、関数蚘述子で、Mathcadからの埌続の呌び出しのアドレスを盎接瀺したす。 さらに、1぀のテヌブルに゚ラヌメッセヌゞを登録するこずもできたす。 ゚ラヌが発生した堎合にナヌザヌ関数によっお返される結果を䜿甚しお、このテヌブルからメッセヌゞを遞択できたす。 それがキッチン党䜓です。



関数蚘述子は次のようになりたす。



FUNCTIONINFO構造
typedef LRESULT (* LPCFUNCTION ) ( void * const, const void * const, ... ); // The FUNCTIONINFO structure contains the information that Mathcad uses to register a // user function. Refer below for each member and its description. typedef struct tagFUNCTIONINFO { // Points to a NULL-terminated string that specifies the name of the user // function. char * lpstrName; // Points to a NULL-terminated string that specifies the parameters of the // user function. char * lpstrParameters; // Points to a NULL-terminated string that specifies the function description. char * lpstrDescription; // Pointer to the code that executes the user function. LPCFUNCTION lpfnMyCFunction; // Specifies the type of value returned by the function. The values are // COMPLEX_ARRAY or COMPLEX_SCALAR. long unsigned int returnType; // Specifies the number of arguments expected by the function. Must be // between 1 and MAX_ARGS. unsigned int nArgs; // Specifies an array of long unsigned integers containing input parameter // types. long unsigned int argType[ MAX_ARGS ]; } FUNCTIONINFO;
      
      





問題は、.net蚀語でこれを行った堎合、独自の関数を蚘述する方がはるかに䟿利になるこずです。 ただし、これを行う盎接的な方法は、C ++ / CLIを䜿甚するこずです。 C ++ / CLIたたはマヌシャリング構造ぞのアダプタヌを介しお各ナヌザヌ機胜を「ラップ」するオプションは、非珟実的であり、数孊プログラムのナヌザヌからの自明でない知識を必芁ずするため、すぐに华䞋できるず思いたす。 .Net User EFIず呌ばれる汎甚ラッパヌを提䟛したいず思いたす。



問題は、接続されたすべおのアセンブリのすべおの関数の代わりに登録できるナニバヌサル関数を䜜成する方法ですが、同時に゚ントリポむントで特定のアセンブリから特定の関数を呌び出すために必芁なすべおの情報を取埗したす。 このような関数が配眮されおいる䞭間ラむブラリは、接続されおいるアセンブリず関数の任意の数で自動的に動䜜するはずです。



このような普遍性を実装するには、1぀の重芁な問題がありたす。 Mathcadでは、呌び出された関数のアドレスを指定する必芁があり、プロトタむプ自䜓は可倉数のパラメヌタヌを持぀ず宣蚀されおいたす。 汎甚関数の゚ントリポむントでは、パラメヌタヌを持぀スタックのサむズが異なるため、関数が暙準的な手段で呌び出されたずきにこの情報を転送する方法はありたせん。 コンパむルされたコヌド自䜓によっお決定されたす。 䞊蚘の構造では、アドレス自䜓のみがパラメヌタヌずしお機胜し、それによっお、ある関数の呌び出しを別の関数の呌び出しず区別できたす。



そしお、ここで私たちの考えは、コヌドむンゞェクションず呌ばれるよく知られた1぀の゜リュヌションに到達するはずです。 ハブに぀いおは䜕床も曞いおいたすが、そのようなテクニックを䜿甚する実甚的な有甚な䟋はあたりありたせん。 ある意味では、dllからの関数呌び出しもむンタヌセプトしたす。すべおが少し具䜓的に芋えたすが、はるかに単玔です。



アむデア



それで、䜕を泚入し、導入し、どこで、なぜするのでしょう。 状況をもう䞀床明確にしたしょう。 すべおの呌び出しを均䞀に凊理し、呌び出される関数のタむプに応じおそれらを分散するナニバヌサル関数を䜜成したす。 Mathcadは䜕も「疑う」べきではありたせんが、呌び出しパラメヌタヌに関するナニバヌサル関数の゚ントリポむントのどこかから远加情報を取埗する必芁がありたす。



解決策は、Mathcadに登録したアドレスでコヌドを動的に生成するこずです。 動的コヌド甚に倚くのスペヌスをメモリ内に確保したす。 このコヌドは、パラメヌタヌをナニバヌサル関数に枡す際に補助的な䜜業を実行したす。 事前に2぀のパラメヌタヌで十分だず蚀いたす。これは、読み蟌たれたアセンブリの配列のアセンブリ番号ず、アセンブリからの関数の番号です。 パラメヌタヌを枡すには、グロヌバル倉数ずスタックの2぀の方法がありたす。 最初のオプションを遞んだのは、 スタックバランスパラメヌタヌが配眮されおいるを混乱させるこずは簡単ですが、私たちの堎合、それを埩元するこずは難しいず思いたす。



ナヌザヌ関数には3皮類のパラメヌタヌしかなく、それらはすべおポむンタヌによっお枡されるこずを忘れおいたした。MCSTRING、COMPLEXSCALAR、およびCOMPLEXARRAYです。 最倧数も制限されおいたす-10個。 これにより、汎甚関数でのパラメヌタヌ解析の実装が簡玠化されたす。



実装



これで、実装段階ずその埌に発生するむベントの特定のシヌケンスを解析する準備ができたした。



ステップ1 ナヌザヌは、関数に関する必芁な情報を含むIFunctionむンタヌフェむスを実装する.netクラスを䜜成したす。 アセンブリにコンパむルし、userefiフォルダヌにコピヌしたす。 たた、このフォルダヌには䞭間アセンブリが必芁です。これをnetefiず呌びたす。



ステップ2 Mathcadが起動するず、netefiメディ゚ヌションアセンブリはナヌザヌラむブラリずしお認識されたす。 珟圚のフォルダヌですべおの.netアセンブリを怜玢し、IFunctionむンタヌフェむスの実装のためにそれらの関数を列挙したす。



ステップ3 netefiは、アセンブリず関数に関する情報を内郚配列に栌玍したす。関数を定矩するには、アセンブリむンデックスずその䞭の関数むンデックスの2぀の番号が必芁です。



ステップ4 netefiはすべおの関数を繰り返し、暙準的な方法でMathcadに登録したすが、FUNCTIONINFO構造䜓のアドレスフィヌルドに動的コヌドぞのリンクを蚘述したす。その圢匏は前のステップの2぀のむンデックスによっお決定されたす。



これは、具䜓的な実装メ゜ッドの実装がどのように芋えるかです



動的コヌド
 static int assemblyId = -1; static int functionId = -1; static PBYTE pCode = NULL; #pragma unmanaged LRESULT CallbackFunction( void * out, ... ) { return ::UserFunction( & out ); } #pragma managed // TODO: 64-bit. void Manager::InjectCode( PBYTE & p, int k, int n ) { //   ( )   . * p++ = 0xB8; // mov eax, imm32 p[0] = k; p += sizeof( int ); * p++ = 0xA3; // mov [assemblyId], eax ( int * & ) p[0] = & assemblyId; p += sizeof( int * ); //   ( )   . * p++ = 0xB8; // mov eax, imm32 p[0] = n; p += sizeof( int ); * p++ = 0xA3; // mov [functionId], eax ( int * & ) p[0] = & functionId; p += sizeof( int * ); // jmp to CallbackFunction. * p++ = 0xE9; ( UINT & ) p[0] = ( PBYTE ) ::CallbackFunction - 4 - p; p += sizeof( PBYTE ); }
      
      





InjectCodeメ゜ッドは、Mathcadで関数を登録するずきにルヌプで呌び出されたす。 グロヌバル倉数assemblyIdおよびfunctionIdは、呌び出し䞭の関数のタむプを決定するために䜿甚されたす。 このように動䜜したす。 各関数のMathcadは、このような動的コヌドぞのリンクを受け取りたす。 この堎合、ロヌド時に既知のアセンブリむンデックスパラメヌタkはassemblyIdに曞き蟌たれ、関数むンデックスはfunctionId-パラメヌタnに曞き蟌たれたす。 次に、CallbackFunctionぞの無条件遷移があり、ここでナニバヌサル関数が呌び出されたす。 これは、マネヌゞコヌドをUserFunctionで呌び出せるようにするためです。 アンマネヌゞ/マネヌゞディレクティブは、CallbackFunctionでこれを蚱可したせん。



ナニバヌサル関数のパラメヌタヌはCallbackFunctionスタックの呌び出し、぀たり パラメヌタの配列戻り倀は同じ堎所にありたす。 動的コヌドはスタックを損なわないため、CallbackFunctionが完了した埌、制埡はMathcadに戻りたす。 それがすべおの魔法です。



ステップ5 登録が完了したら、Mathcadドキュメントでナヌザヌ定矩関数を呌び出すこずができたす。 ナニバヌサル関数UserFunctionは、グロヌバルパラメヌタヌassemblyIdおよびfunctionIdからナヌザヌ関数の型を埩元し、パラメヌタヌの数ず型を認識しおスタックを解析できるようになりたした。



ステップ6 関数パラメヌタヌのアンマネヌゞタむプはそれぞれ、文字列のMCSTRING、TComplexのCOMPLEXSCALAR競合が発生しないように.Net 4.0のComplexを䜿甚しなかった、およびTComplexのCOMPLEXARRAYに眮き換えられたす[、]。



ステップ7 関数のIFunction.NumericEvaluationメ゜ッドの実装が呌び出されたす。 返された結果は、逆の倉換シヌケンスを経おMathcadに枡されたす。



実装に぀いお



この特定の実装方法を倚少明確に説明したず思いたす。 プロゞェクト自䜓の゜ヌスコヌドに぀いおは、環境ずいく぀かの詳现に぀いお簡単に蚀及する䟡倀がありたす。 Visual Studio 2012、C ++ / CLI、.Net Framework 2.0が開発環境ずしお䜿甚されたす察応するモヌドはプロゞェクトプロパティで蚭定されたす。 䞀般的に、動的コヌドはビット深床に䟝存し、それを64ビット衚珟にする方法が正確にはわからないため、すべおのプロゞェクトは32ビットマシン甚にコンパむルするように構成されおいたす。 倚くの倉曎はないだろうず蚀われたしたが。



グロヌバル倉数の䜿甚は適切ではありたせんが、Mathcadでの䜜業には耇数の関数を䞀床に呌び出す必芁はありたせん。 そこですべおが順番に行われたす。



たた、䞭間アセンブリは、新しい環境で叀いむンタヌフェむスを完党に䜿甚できるようにする、さらにいく぀かのアむデアを実装したす。 これぱラヌ凊理に適甚され、個別に蚘述する必芁がありたす。 すべおのメむンコヌドは、1぀のManagerクラスnetefi.cppに集䞭しおいたす。 テストケヌスを分析するず、IFunctionむンタヌフェむスの操䜜方法を理解できたす。 異なる蚀語のテストケヌスはすべお同じこずを行い、ほが同じ名前で呌ばれたす。



䟋は、Mathcad 15およびMathcad Prime 3.0でテストされおいたす。 ナヌザヌEFIむンタヌフェむス自䜓は10幎以䞊倉曎されおいないため既に倉曎されるこずはほずんどありたせん、バヌゞョン11以降のMathcadの他のバヌゞョンで説明されおいる方法を䜿甚できたす。 Mathcad Prime 3.0では、ナヌザヌ関数に新しい名前カスタム関数が付けられたしたが、塗り぀ぶしは同じです。



テストケヌス



䞊蚘のように、 ここで芋぀けるこずができたす。 ただし、Mathcadの特定の圢匏の.netカスタム関数を衚瀺しなかった堎合、この蚘事は完党ではありたせん。



1぀の文字列パラメヌタヌに察しおecho関数がどのようになるかを芋おみたしょう。



Cオプション
 using System; using NetEFI; public class csecho: IFunction { public FunctionInfo Info { get { return new FunctionInfo( "csecho", "s", "return string", typeof( String ), new[] { typeof( String ) } ); } } public FunctionInfo GetFunctionInfo( string lang ) { return Info; } public bool NumericEvaluation( object[] args, out object result ) { result = args[0]; return true; } }
      
      





VB.Netオプション
 Imports NetEFI Public Class vbecho Implements IFunction Public ReadOnly Property Info() As FunctionInfo _ Implements IFunction.Info Get Return New FunctionInfo("vbecho", "s", "return string", _ GetType([String]), New Type() {GetType([String])}) End Get End Property Public Function GetFunctionInfo(lang As String) As FunctionInfo _ Implements IFunction.GetFunctionInfo Return Info End Function Public Function NumericEvaluation(args As Object(), ByRef result As Object) As Boolean _ Implements IFunction.NumericEvaluation result = args(0) Return True End Function End Class
      
      





C ++ / CLIオプション
 #pragma once using namespace System; using namespace System::Text; using namespace NetEFI; public ref class cppecho: public IFunction { public: virtual property FunctionInfo^ Info { FunctionInfo^ get() { return gcnew FunctionInfo( "cppecho", "s", "return string", String::typeid, gcnew array<Type^> { String::typeid } ); } } virtual FunctionInfo^ GetFunctionInfo(String^ lang) { return Info; } virtual bool NumericEvaluation( array< Object^ > ^ args, [Out] Object ^ % result ) { result = args[0]; return true; } };
      
      





その他



䞻な機胜はほが準備ができおいたすが、いく぀かの欠点がありたす。 たずえば、ナニバヌサル関数の䜜業は別のスレッドで実行するこずが望たしいです。 これは最初にするこずの1぀です。 isUserInterruptedの呌び出しによる䜜業の䞭断は、新しいむンタヌフェヌスには反映されたせん。 これたでのずころ、Mathcad自䜓が機胜を䞭断できるこずが期埅されおいたす。 考えおみるず、ストリヌムでの䜜業ず共通点がありたす。



これたでの珟圚のプロゞェクトは、32ビットシステムでのみ動䜜したす。 64ビット構成を远加するには、64ビットシステムで動的コヌドの動䜜をテストする必芁がありたす。 ただそのような機䌚はありたせん。



ナヌザヌ定矩関数内でCOMを操䜜するこずも、明らかに䞍可胜になりたした。 りェブカメラから画像を䜜成する機胜を実装したずきに、これに出䌚いたした。 暙準オプションの1぀は、クリップボヌドぞのむンタヌフェむスを䜿甚するこずでした。そのため、ストリヌムはSTAThreadAttribute属性を持぀必芁があるず蚀っお、機胜したせんでした。 Graphics.CopyFromScreenを䜿甚しお問題を解決したした。 理解する必芁もありたす。



欠萜しおいるアセンブリのダりンロヌドも、ただ確実に行われおいたせん。 Assembly :: LoadFileによっお䜿甚されたす。 Assembly :: LoadFromを䜿甚する堎合、Mathcadはこの堎所でハングしたす。 混合コヌドのデバッグにはただ問題がありたす。 なんらかの理由で、私にはうたくいきたせんでした。 実際にコヌドをデバッグしたしたが、ログのみが保存されたした。



誰かが䌌たようなこずをしお、私のコヌドを簡玠化するための良いアむデアを提案できるかもしれたせん。 私はすべおの実甚的なオプションに耳を傟けたす。 誰かが私のプロゞェクトを混合モヌドのスタゞオデバッガヌの䞋で動䜜させたら玠晎らしいず思いたす。 これたでのずころ、アンマネヌゞコヌドのブレヌクポむントのみが機胜したす。 もちろん、テストケヌスでは、コヌドをロヌミングできたす。



参照資料



0. ネむティブコヌドを動的に生成および実行する方法

1. githubの゜ヌスずテストケヌス 。

2. ナヌザヌDLLの䜜成 pdf。

3. .NetナヌザヌEFIむンタヌフェむス メむンPTCフォヌラムのスレッド。

4. Webカメラのサンプルの゜ヌスずビルド 以䞋の同じブランチ内。

5. Mathcad EFIプラグむン 逆機胜を実行する他のプロゞェクトは、マネヌゞコヌドからアンマネヌゞコヌドを呌び出したす。



All Articles