0

I'm trying to create a .exe file and this file has to call a function that is allocated in another DLL. I can load the DLL correctly and the type and the method are referenced correctly but it gives an exception when I try to invoke the Main method:

Common Language Runtime detected an invalid program.

I know when i try to call a method from the other DLL is causing the error because i try to run this program without that instruction and there was no throw exception. I'm going to put the whole code here (this is for studying purposes).

static void Main() {
        String path=createTestMethodDLL()+"\\Testing.dll";
        AssemblyName asmName = new AssemblyName();
        asmName.Name = "HelloReflectionEmit";
        AppDomain appDom = Thread.GetDomain();
        // Create AssemblyBuilder with "RunAndSave" access
        AssemblyBuilder asmBuilder = appDom.DefineDynamicAssembly(asmName,
                                        AssemblyBuilderAccess.RunAndSave);
        // Assembly filename
        string filename = asmName.Name + ".exe";
        // Create ModuleBuilder object
        ModuleBuilder modBuilder = asmBuilder.DefineDynamicModule(
                                                asmName.Name, filename);

        // Define "public class Hello.Emitted"
        //
        TypeBuilder typeBuilder = modBuilder.DefineType("Hello.Emitted",
                                    TypeAttributes.Public | TypeAttributes.Class);
        // Define "public static int Main(string[] args)"
        //

        MethodBuilder methBuilder = typeBuilder.DefineMethod("Main",
                                            MethodAttributes.Public |
                                            MethodAttributes.Static,
                                            typeof(int),
                                            new Type[] { typeof(string[]) });
        MethodBuilder methBuilder2 = typeBuilder.DefineMethod("test",
                                            MethodAttributes.Public |
                                            MethodAttributes.Static);

        ILGenerator ilGen = methBuilder.GetILGenerator();
        ILGenerator ilGen2 = methBuilder2.GetILGenerator();
        ilGen2.Emit(OpCodes.Ldstr, "test method!");
        ilGen2.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string[]) }));
        ilGen2.Emit(OpCodes.Ret);

        // Define a call to System.Console.WriteLine, passing "Hello World"
        var DLL=Assembly.LoadFile(path);
        Type [] ty=DLL.GetExportedTypes();
        ilGen.Emit(OpCodes.Ldstr, "Hello, World!");
        ilGen.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string[]) }));
        ilGen.EmitCall(OpCodes.Call,methBuilder2,new Type[] { });

        //problem Down here//
        ilGen.EmitCall(OpCodes.Call, ty[0].GetMethod("testFunction"),new Type[] {}); 

        ilGen.Emit(OpCodes.Ldc_I4_0);
        ilGen.Emit(OpCodes.Ret);
        asmBuilder.SetEntryPoint(methBuilder, PEFileKinds.ConsoleApplication);
        // Save assembly to file
        asmBuilder.Save(filename);
        ReflectOnAssembly(asmName, asmBuilder, "Main", "Hello.Emitted");
        appDom.ExecuteAssembly(filename);
        Console.WriteLine("Finished executing {0}", asmName.Name);
        Console.ReadLine();

        LoadAssembly(filename);

    }

    private static String createTestMethodDLL() {
        AssemblyName asmName = new AssemblyName();
        asmName.Name = "Testing";
        AppDomain appDom = Thread.GetDomain();
        // Create AssemblyBuilder with "RunAndSave" access
        AssemblyBuilder asmBuilder = appDom.DefineDynamicAssembly(asmName,
                                        AssemblyBuilderAccess.RunAndSave);
        // Assembly filename
        string filename = asmName.Name + ".dll";

        // Create ModuleBuilder object
        ModuleBuilder modBuilder = asmBuilder.DefineDynamicModule(
                                                asmName.Name, filename);

        // Define "public class Hello.Emitted"
        //
        TypeBuilder typeBuilder = modBuilder.DefineType("ConsoleApp1.Testing",
                                    TypeAttributes.Public | TypeAttributes.Class);
        // Define "public static int Main(string[] args)"
        //
        MethodBuilder methBuilder = typeBuilder.DefineMethod("testFunction",
                                            MethodAttributes.Public |
                                            MethodAttributes.Static);
        ILGenerator ilGen = methBuilder.GetILGenerator();

        ilGen.Emit(OpCodes.Ldstr, "testing!");
        ilGen.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string[]) }));
        ilGen.Emit(OpCodes.Ldc_I4_0);
        ilGen.Emit(OpCodes.Ret);

        // Create type
        Type t = typeBuilder.CreateType();
        // Reflect
        //ReflectOnAssembly(asmName, asmBuilder,"testFunction", "ConsoleApp1.Testing");
        asmBuilder.SetEntryPoint(methBuilder, PEFileKinds.ConsoleApplication);
        // Save assembly to file
        asmBuilder.Save(filename);
        //LoadAssembly(filename);
        Assembly asm = Assembly.GetExecutingAssembly();
        return System.IO.Path.GetDirectoryName(asm.Location);
    }

    private static void ReflectOnAssembly(AssemblyName asmName, AssemblyBuilder asmBuilder,String methodName,String typeName) {
        Console.WriteLine("Now, use reflection on assembly {0}:", asmName.Name);
        foreach (Type type in asmBuilder.GetTypes()) {
            Console.WriteLine("Type {0}", type);
            foreach (MethodInfo mi in type.GetMethods(BindingFlags.Public
                                                      | BindingFlags.NonPublic
                                                      | BindingFlags.Instance
                                                      | BindingFlags.Static)) {
                Console.WriteLine("Method {0}", mi.ToString());
            }
            Console.WriteLine("Invoking Main:");
            MethodInfo mainMethod = asmBuilder.GetType(typeName).GetMethod(methodName);
            if (mainMethod != null)
                // First param = this, second param = method parameters
                mainMethod.Invoke(null, new object[1] { null });
        }
    }

    private static void LoadAssembly(string filename) {
        Assembly assembly = Assembly.LoadFrom(filename);
        // Get public and non public types from assembly
        Type[] types = assembly.GetTypes();
        foreach (Type t in types) {
            Console.WriteLine(t.Name);
        }
    }
  • 1
    testFunction appears to take a string[] argument. But you are not providing it. An easy way to get this right is to first write the method in C# and use ildasm.exe to look at the generated IL. – Hans Passant Nov 15 '18 at 22:17
  • Thanks @HansPassant for the suggestion. I'm going to do that right now – Manuel Dias Nov 15 '18 at 22:24
  • @HansPassant the ildasm wasn't much help... – Manuel Dias Nov 15 '18 at 23:26
  • Hmm, if you (say) pass the string[] argument of Main() to testFunction then you should see the extra ldarg.0 – Hans Passant Nov 15 '18 at 23:32
  • @HansPassant i have updated my post with the whole code. I know when i try to call the function from the DLL is the problem, i think i'm not doing it correctly must certain... I've googled for hours and i couldn't find an answer – Manuel Dias Nov 15 '18 at 23:41
  • You keep focusing on the wrong problem, it doesn't have anything to do with the DLL. The IL you generate is wrong, it doesn't provide the argument that testFunction() needs. That imbalances the stack, forcing the jitter to crash your program with "invalid program". You save it to a file, good, that lets you see this another way. Run peverify.exe from the Developer Command Prompt and it tells you about it up front. – Hans Passant Nov 15 '18 at 23:50
  • @HansPassant i know I'm not generating the IL correctly and the problem is i don't know how to correctly generate it xD – Manuel Dias Nov 16 '18 at 00:13

0 Answers0