2009-11-28 12 views
5

基本的には、イベント名を文字列として受け入れて、EventInfoを取得します。次に、リフレクションを使用してイベントハンドラの型とイベントの引数型を発見し、その型の新しいデリゲート(myEventHandler)を作成し、イベントにフックします。いつまでかmyEventHandlerが呼び出されると、私はダウンキャストして、引数をハンドラに渡す必要があります。ILデリゲートインスタンスを呼び出すために発行しますか?

私のコードは以下の通りです。 「ハンドラ」はmyEventHandlerによって呼び出される必要があります。いつでも「d」が呼び出されます。私はいくつかのReflectionをそこに置く必要がありますコードをどこに置くか?何かご意見は?

EventHandler handler = delegate(object sender, EventArgs eventArgs) 
{ 
    //something will happen here         
}; 

Type[] typeArgs = { typeof(object), derivedEventArgsType }; 

DynamicMethod myEventHandler = new DynamicMethod("", typeof(void), typeArgs); 
var ilgen = myEventHandler.GetILGenerator(); 

//What should be the IL code here to 
//cast derviedEventArgs to EventArgs and 
//invoke the 'handler' above?????? 
ilgen.Emit(OpCodes.Pop); 
ilgen.Emit(OpCodes.Ret); 



Delegate d = dynamic.CreateDelegate(derviedEventHandlerType); 

//addMethod is the add MethodInfo for an Event 
addMethod.Invoke(target, new object[] { d }); 

編集:リフレクターを介した観察に基づいて。

反射手動符号化されたシナリオのためのコードを生成

.method public hidebysig instance void <Main>b__1(object sender, class ConsoleApplication2.MyEventArgs e) cil managed 
{ 
    .maxstack 8 
    L_0000: nop 
    L_0001: ldarg.0 
    L_0002: ldfld class [mscorlib]System.EventHandler ConsoleApplication2.Program/<>c__DisplayClass3::handler 
    L_0007: ldarg.1 
    L_0008: ldarg.2 
    L_0009: callvirt instance void [mscorlib]System.EventHandler::Invoke(object, class [mscorlib]System.EventArgs) 
    L_000e: nop 
    L_000f: ret 
} 

であり、これは、私はそれに基づいてしようとしたものです。

ilgen.Emit(OpCodes.Nop); 
ilgen.Emit(OpCodes.Ldarg_0); 
ilgen.Emit(OpCodes.Ldfld,eh.GetType().GetField("handler")); 
ilgen.Emit(OpCodes.Ldarg_1); 
ilgen.Emit(OpCodes.Ldarg_2); 
ilgen.EmitCall(OpCodes.Callvirt,eh.handler.Method, 
       new Type[]{ typeof(object), typeof(EventArgs) }); 
ilgen.Emit(OpCodes.Nop); 
ilgen.Emit(OpCodes.Ret); 

しかし、これは、実行時エラーの原因となっている:

'Calling convention must be varargs'

たぶん私は何かが欠けていますが、ILに優れた外観を持っている必要があります。

+2

ここでのトリックは、常にC#で必要なコードを記述し、ILを見るためにリフレクタ/ ILDASMを使用することです。私はld、castclass、そしてcallvirtの組み合わせを推測するだろう –

+0

すごく合意した。私はそのルートを取っているだろうが、どんなリフレクションもそうではないかと思っていたので、Ninjasはすぐにこれを指摘するかもしれない。 – amazedsaint

+0

もう一度見て - "ハンドラ"はどこにあるの? argsと比較して?私はそれを一緒にするのは苦痛になるだろうと思っています。 C#のバージョンではキャプチャクラスが使用されているようですが、動的メソッドは静的なので、どんな状態でも押し込むことはありません。 –

答えて

5

私は非常に複雑なものでした。バリーケリーhad the right idea

static T CastDelegate<T>(Delegate src) 
    where T : class 
{ 
    return (T)(object)Delegate.CreateDelegate(
     typeof(T), 
     src.Target, 
     src.Method, 
     true); // throw on fail 
} 

私のテストケースのために働きます。

5

OK - これは役立ちます。標準パターンと一致する限り、デリゲート型を切り替えるILを生成します。必要な場合にのみ、キャストクラスを追加します(MouseEventArgsからEventArgsへ行く必要がない場合は逆の方向です)。あなたは明確に反射に取り組んでいるので、私はジェネリックスを使用していない(これは物事をより困難にするだろう)。

キャプチャクラスを使用する代わりに、そのメソッドはキャプチャするデータに属していると見なし、状態をarg0として使用します。それが邪悪なのか巧妙なのかを決めることはできないので、私は「クレビル」で行くつもりです。明らかに

using System; 
using System.Reflection; 
using System.Reflection.Emit; 
using System.Threading; 
using System.Windows.Forms; 

class Program { 
    static ParameterInfo[] VerifyStandardHandler(Type type) { 
     if (type == null) throw new ArgumentNullException("type"); 
     if (!typeof(Delegate).IsAssignableFrom(type)) throw new InvalidOperationException(); 
     MethodInfo sig = type.GetMethod("Invoke"); 
     if (sig.ReturnType != typeof(void)) throw new InvalidOperationException(); 
     ParameterInfo[] args = sig.GetParameters(); 
     if (args.Length != 2 || args[0].ParameterType != typeof(object)) throw new InvalidOperationException(); 
     if (!typeof(EventArgs).IsAssignableFrom(args[1].ParameterType)) throw new InvalidOperationException(); 
     return args; 
    } 
    static int methodIndex; 
    static Delegate Wrap(Delegate value, Type type) { 
     ParameterInfo[] destArgs = VerifyStandardHandler(type); 
     if (value == null) return null; // trivial 
     if (value.GetType() == type) return value; // already OK 
     ParameterInfo[] sourceArgs = VerifyStandardHandler(value.GetType()); 
     string name = "_wrap" + Interlocked.Increment(ref methodIndex); 
     Type[] paramTypes = new Type[destArgs.Length + 1]; 
     paramTypes[0] = value.GetType(); 
     for (int i = 0; i < destArgs.Length; i++) { 
      paramTypes[i + 1] = destArgs[i].ParameterType; 
     } 
     DynamicMethod dyn = new DynamicMethod(name, null, paramTypes); 
     MethodInfo invoker = paramTypes[0].GetMethod("Invoke"); 
     ILGenerator il = dyn.GetILGenerator(); 
     il.Emit(OpCodes.Ldarg_0); 
     il.Emit(OpCodes.Ldarg_1); 
     il.Emit(OpCodes.Ldarg_2); 
     if (!sourceArgs[1].ParameterType.IsAssignableFrom(destArgs[1].ParameterType)) { 
      il.Emit(OpCodes.Castclass, sourceArgs[1].ParameterType); 
     } 
     il.Emit(OpCodes.Call, invoker); 
     il.Emit(OpCodes.Ret); 
     return dyn.CreateDelegate(type, value); 
    } 
    static void Main() { 
     EventHandler handler = delegate(object sender, EventArgs eventArgs) { 
      Console.WriteLine(eventArgs.GetType().Name); 
     }; 
     MouseEventHandler wrapper = (MouseEventHandler)Wrap(handler, typeof(MouseEventHandler)); 
     MouseEventArgs ma = new MouseEventArgs(MouseButtons.Left, 1, 1, 1, 1); 
     wrapper(new object(), ma); 

     EventHandler backAgain = (EventHandler)Wrap(wrapper, typeof(EventHandler)); 
     backAgain(new object(), ma); 
    } 
} 

あなたはまだ、通常の方法(Delegate.CreateDelegateなど)を使用して、イベントにデリゲートを生成する必要がありますが、その後EventHandler、または逆にそれをラップすることができます。

+0

これを試してみる必要がありますが、より良い答えを得ることができるかどうかはわかりません。したがって、これを答えとして受け入れる。ありがとう:) – amazedsaint

+0

さて、確認されています。私が望むものを解決しました。 – amazedsaint

関連する問題