文書化されていない機能の文書化されていない機能:別のスレッドにrefを渡す

フィールドへのリンクを別のストリームに保存/転送する方法は? 」 論理的な仮定は「メソッドにrefを渡して保存します。 やめて、オ〜 はい、 refは保存されません(まだ、クロージャーを使用することはできません。そのため、そのようなメソッド内で関数を作成し、そこからストリームを作成することも失敗します)。 しかし、文書化されていないキーワード__makeref使用して、 refTypedReferenceに変換できます。 残念ながらTypedReferenceはフィールドに直接格納できず、 System.Objectから継承しないため、通常のメソッドでのキャストも不可能です(実際、使用には多くの制限が課せられます)。 行き止まりのようです。 しかし、それだけではありません-Type.Referenceプロパティを持つRuntimeArgumentHandleがありますが、1つの例外があります-System.Objectでのトリッキーなキャストの後、それが作成されたスタックのフレームが生きている限り、それを使用できます。 この投稿について。





型付き参照



TypedReference自体は驚くべき小さなものです-refをラップして別のメソッドを渡すことができます(つまり、メソッドのパラメーターの1つがref 'ohm)。 ただし、この構造のメソッドでは値を設定できません-NotSupportedExceptionは適切なメソッドを呼び出すことを想定しています。 しかし、それは問題ではありません。値を取得するだけでなく、設定することもできる__refvalueキーワードがあります。 しかし、それはかなり奇妙に見えます:



void Out(ref int someInt) { Input(__makeref(someInt)); } void Input(TypedReference @ref) { int val = __refvalue(@ref, int);//  __refvalue(@ref, int) = 0;//   someInt }
      
      







型はハンドルによって設定されるという事実にもかかわらず、たとえば、 int文字列に保存することはできません。それにもかかわらず、型に属することの検証は実行されます。

それだけです、 TypedReferenceはクロージャーでも使用できません。そのため、 TypedReferenceでクロージャーを使用して何かを作成するためにも機能しません。



RuntimeArgumentHandle



それはparamsに他なりません。 実際、これはTypedReferenceのリスト( ArgIteratorを構築することによりアクセスされます)ですが、作成されることもあります...それを記述する方法すらわかりません:



 void Out(int something) { Input(__arglist(something)); } void Input(__arglist) { new ArgIterator(__arglist); }
      
      







同時に、デリゲートが宣言されている場合、 __ arglistキーワードをデリゲートで使用することはできません。 ただし、 RuntimeArgumentHandleは可能です(ただし、パラメーターとしてのみ、 TypedReferenceおよびRuntimeArgumentHandleをメソッドから返すことはできません)。 __arglist()をデリゲートを呼び出す引数として使用することもできませんが、 __ arglistは可能です。 このやや曖昧な言葉遣いの意味は、例によってよりよくサポートされています。



 delegate void ArgWarrior(RuntimeArgumentHandle argh); void Out(int something) { (new ArgWarrior(u => { } ))(__arglist(someting));//  Input(new ArgWarrior(u => { } ), __arglist(someting); } void Input(ArgWarrior argh, __arglist) { argh(__arglist);//   }
      
      







それで、このマレゾンのバレエの要点であるデリゲートに到達しました。



精巧なモッキングデリゲートの方法としての_methodPtrAuxの操作



_methodPtrAuxは、デリゲート型の4番目のフィールドであり、ここで重要な役割を果たします。 ポイントは何ですか? 一番下の行は、 _methodPtrAuxが既にjitメソッドへのポインターを格納していることです。 そのポインターによって任意のアンマネージコードを記述したので、このアンマネージコードを実行できます。 しかし、これはポイントではありません。 デリゲートは_methodPtrAuxの値を変更した後でも使用可能なままであり、呼び出されると、このフィールドの値が示す場所に制御が正確に移動します。 したがって、異なる入力パラメーターを持つ2つのデリゲートを使用して、デリゲートaからのポインターをデリゲートbからのポインターに置き換えることができます。 引数のセットが異なっていても、すべてが機能します。 重要な点は、対応する引数のタイプが異なっていても、clrはアラームを鳴らさないことです。intは文字列にキャストされ、 すべての希望は満たされ、誰も 気分を害することはありません 、または... RuntimeArgumentHandleSystem.Objectに変換されます:



  delegate void Encast(RuntimeArgumentHandle @ref); delegate void Uncast(object @object); static void UseWith(Encast en, __arglist) { en(__arglist); } static object m_storedRef; static void Engage(ref object @object) { Encast en = new Encast(@ref => { }); Uncast un = new Uncast(o => { m_storedRef = o;//     <b>en</b>. }); typeof(Encast).GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)[3].SetValue(en, typeof(Uncast).GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)[3].GetValue(un));//   en UseWith(en, __arglist(__makeref(@object))); // }
      
      







ラムダからわかるように、結果の値をすぐに静的フィールドに保存します。 はい、理解できない制限が1つあります。oを非静的フィールドに保存すると、clr(読み取り/書き込み保護メモリ)を削除できます。 ターゲットフィールドがフィールドではなく、静的フィールド(たとえば、ディクショナリ)に格納されているオブジェクトフィールドであっても、すべてがスムーズに進むはずです。 同時に、デバッガーではラムダ引数がやや奇妙に見えます。表示すると、 "{object}"(引用符なし)のみが表示され、それ以上は表示されません。 型を抽出しようとしたり、 Stringにキャストしようとしてもうまくいきません(clrをドロップできます)。
非表示のテキスト
しかし、この種のフォーカスをTypedReference回すと 、何か他の興味深いものを見ることができます(読者が自分で勉強できるようにしておきます。intに入れてみるのもおもしろいです)。


逆変換も同様に実行されます。 スタックフレームの保存は、モニターを使用して行われます。



  static object m_locker = new object(); //... Monitor.Enter(m_locker); Monitor.Exit(m_locker);
      
      







m_lockerは別のスレッドから事前にブロックされているため、実行は一時停止します。 RuntimeArgumentHandleは、破損することなくスタックに残ります。

完全なプログラムコードは次のようになります。



 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; namespace ThreadJiggler { class Program { delegate void Encast(RuntimeArgumentHandle @ref); delegate void Uncast(object @object); static object m_storedRef; static object m_locker = new object(); static bool m_useFlag; static void Main(string[] args) { object @v = "means \"vendetta\""; Victim1(ref @v); Console.WriteLine(@v); } static void UseWith(Encast en, __arglist) { en(__arglist); } static Thread m_someThread; static void Victim1(ref object @object) { Thread t = new Thread(() => { Monitor.Enter(m_locker); { for (; !m_useFlag; ) { Thread.Sleep(10); } Encast en = new Encast(@ref => { TypedReference tr = new ArgIterator(@ref).GetNextArg(); __refvalue( tr, object) = 0; }); Uncast un = new Uncast(o => { m_storedRef = o; }); typeof(Uncast).GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)[3].SetValue(un, typeof(Encast).GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)[3].GetValue(en)); un(m_storedRef); } Monitor.Exit(m_locker); }); t.IsBackground = false; t.Start(); { Encast en = new Encast(@ref => { }); Uncast un = new Uncast(o => { m_storedRef = o; m_useFlag = true; Monitor.Enter(m_locker); Monitor.Exit(m_locker); }); typeof(Encast).GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)[3].SetValue(en, typeof(Uncast).GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)[3].GetValue(un)); UseWith(en, __arglist(__makeref(@object))); } } } }
      
      







Mainの最後で、 @ vの値が0に変更されたことがわかります



All Articles