0

I am trying this simple example on creating a generic method using Reflection.Emit but it is causing an exception when calling Invoke and I can't find the problem.

public class Program
{
    public static void Main(string[] args)
    {
        AppDomain appDomain = AppDomain.CurrentDomain;
        AssemblyName name = new AssemblyName("MyAssembly") { Version = new Version("1.0.0.0") };
        AssemblyBuilder ab = appDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.RunAndSave);
        ModuleBuilder mb = ab.DefineDynamicModule("MyModule", "MyAssembly.dll");
        TypeBuilder tb = mb.DefineType("Widget", TypeAttributes.Public);

        // Define a method builder
        MethodBuilder methodBuilder = tb.DefineMethod("MyGenericMethod", MethodAttributes.Public | MethodAttributes.Static);

        // Get generic type parameter builders 
        GenericTypeParameterBuilder[] genericParams =
            methodBuilder.DefineGenericParameters("TKey", "TValue");

        methodBuilder.SetSignature(typeof(int), null, null,
            genericParams,
            null, null);
        methodBuilder.DefineParameter(1, ParameterAttributes.None, "key");
        methodBuilder.DefineParameter(2, ParameterAttributes.None, "val");

        ILGenerator gen = methodBuilder.GetILGenerator();
        MethodInfo writeLineStr = typeof(Console).GetMethod("WriteLine", new[] { typeof(object) });
        gen.Emit(OpCodes.Ldarg_0);
        gen.Emit(OpCodes.Box, genericParams[0]);
        gen.Emit(OpCodes.Call, writeLineStr);
        gen.Emit(OpCodes.Ldarg_1);
        gen.Emit(OpCodes.Box, genericParams[1]);
        gen.Emit(OpCodes.Call, writeLineStr);
        gen.Emit(OpCodes.Ret);

        Type t = tb.CreateType();
        MethodInfo genMeth = t.GetMethod("MyGenericMethod").MakeGenericMethod(typeof(string), typeof(int));
        genMeth.Invoke(null, new object[] { "NumberKey", 100 });
    }
}

When calling genMeth.Invoke, the following exception is thrown

Unhandled Exception: System.Reflection.TargetInvocationException: Exception has been thrown by the t
arget of an invocation. ---> System.InvalidProgramException: Common Language Runtime detected an inv
alid program.
   at Widget.MyGenericMethod[TKey,TValue](TKey key, TValue val)
   --- End of inner exception stack trace ---
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Bool
ean constructor)
   at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Obje
ct[] arguments)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder,
 Object[] parameters, CultureInfo culture)
   at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
   at ReflectionEmitDemo14.Program.Main(String[] args) in d:\Projects\ReflectionEmitDemo\ReflectionE
mitDemo14\Program.cs:line 50
randacun
  • 562
  • 1
  • 6
  • 18

1 Answers1

3

Your method is static and has two arguments: argument 0 and argument 1. But in the generated IL code you're accessing an argument 2 which does not exist.

Another problem is that you're trying to pass an Int32 value to a method that expects a String reference. Box both arguments and pass them to the WriteLine overload that accepts an Object reference.

You also have declared your method to return an Int32, but you're not putting anything on the stack before returning.

Try to write the method in C# and then take a look at what IL the C# compiler emits (for example, using ILspy). It should look like this:

ldarg.0
box !TKey
call void [mscorlib]System.Console::WriteLine(object)

ldarg.1
box !TValue
call void [mscorlib]System.Console::WriteLine(object)

ldc.i4.s 42
ret
dtb
  • 213,145
  • 36
  • 401
  • 431
  • In instance methods, Ldarg_0 pertains to "this" and Ldarg_1 pertains to first parameter. Does that mean that in static methods Ldarg_0 pertains to the first parameter instead? – randacun Jul 23 '13 at 01:19
  • Yes, static methods do not have a "this" parameter. – dtb Jul 23 '13 at 01:20
  • Modified the code based on your comment but exception still shows. – randacun Jul 23 '13 at 01:27
  • Sorry, but I don't know how to convert box !TKey to a C# method call: gen.Emit(OpCodes.Box, ???); – randacun Jul 23 '13 at 01:37
  • Try this: `gen.Emit(OpCodes.Box, genericParams[0]);` – dtb Jul 23 '13 at 01:41
  • Modified code again based on your suggestions. Still no luck :( – randacun Jul 23 '13 at 01:42
  • 1
    Then please do me a favor and write the method in C#, compile it, get the IL using ILspy and add it to your question. I'm just on a mobile phone at the moment and can't check myself. – dtb Jul 23 '13 at 01:45
  • Did you change WriteLine(string) to WriteLine(object)? Do you still get the same error message? – dtb Jul 23 '13 at 01:47
  • Same error message even after changing to object. Will try to decompile first using the tool you suggested (sorry I'm new to this IL stuff). Thanks. – randacun Jul 23 '13 at 01:51
  • Nevermind, found the problem. My method signature returns an int even though the emit statements doesn't return anything. – randacun Jul 23 '13 at 01:55
  • 1
    Try to save generated assembly, and use PEVerify.exe to find what's wrong with your IL. – Jury Soldatenkov Jul 23 '13 at 05:41