views:

305

answers:

2

Basically, I'm accepting an event name as a string, to get the EventInfo. Then, I'm discovering the event handler type and event argument type using reflection, creating a new delegate of that type (myEventHandler), and hooking it up with the event. When ever myEventHandler is invoked, I need to downcast and pass the arguments to the handler.

My code is as below. The 'handler' needs to be invoked via myEventHandler, when ever 'd' is invoked. I need to have some Reflection emit code there where I put ???. Any thoughts?

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 });

Edit: Based on observations via Reflector.

The reflector generated code for a manually coded scenario is

.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 
    }

And this is what I tried based on that.

                    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);

But this is causing a run time error, 'Calling convention must be varargs' :(. Probably I'm missing something, need to have a better look into IL.

+2  A: 

OK - this might help; it generates the IL to switch between delegate types, as long as they match the standard pattern. It adds a castclass only when necessary (so if you are going from a MouseEventArgs to an EventArgs it isn't necessary, but in the reverse direction it is). Since you are clearly working with reflection, I haven't used generics (which would make things harder).

The cheeky bit is that instead of using a capture class, it pretends the method belongs to the data I would capture, and uses the state as arg0. I can't decide if that makes it evil or clever, so I'll go with "clevil".

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);
    }
}

Obviously you still need to generate a delegate to the event using regular methods (Delegate.CreateDelegate etc), but you can then wrap it to an EventHandler, or the reverse.

Marc Gravell
Awesome. Still need to try this out, but I'm not sure whether I can get a better answer. Hence, accepting this as an answer. Thanks :)
amazedsaint
Well, confirmed. Solved what I wanted.
amazedsaint
A: 

It turns out I was vastly over-complicating things! Barry Kelly 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
}

That works for my test cases.

Marc Gravell