2

Greetings! I've been fooling around (a bit) with C# and its assemblies. And so i've found such an interesting feature as dynamic loading assemblies and invoking its class members. A bit of google and here i am, writing some kind of 'assembly explorer'. (i've used some portions of code from here, here and here and none of 'em gave any of expected results).

But i've found a small bug: when i tried to invoke class method from assembly i've loaded, application raised MissingMethod exception. I'm sure DLL i'm loading contains class and method i'm tryin' to invoke (my app ensures me as well as RedGate's .NET Reflector):

alt text

The main application code seems to be okay and i start thinking if i was wrong with my DLL... Ah, and i've put both of projects into one solution, but i don't think it may cause any troubles. And yes, DLL project has 'class library' target while the main application one has 'console applcation' target.

So, the question is: what's wrong with 'em?

Here are some source code:

DLL source:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ClassLibrary1
{
    public class Class1
    {
        public void Main()
        {
            System.Console.WriteLine("Hello, World!");
        }
    }
}

Main application source:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Assembly asm = Assembly.LoadFrom(@"a\long\long\path\ClassLibrary1.dll");

            try
            {
                foreach (Type t in asm.GetTypes())
                {
                    if (t.IsClass == true && t.FullName.EndsWith(".Class1"))
                    {
                        object obj = Activator.CreateInstance(t);
                        object res = t.InvokeMember("Main", BindingFlags.Default | BindingFlags.InvokeMethod, null, obj, null); // Exception is risen from here
                    }
                }
            }
            catch (Exception e)
            {
                System.Console.WriteLine("Error: {0}", e.Message);
            }

            System.Console.ReadKey();
        }
    }
}

UPD: worked for one case - when DLL method takes no arguments:

DLL class (also works if method is not static):

public class Class1
{
    public static void Main()
    {
        System.Console.WriteLine("Hello, World!");
    }
}

Method invoke code:

object res = t.InvokeMember("Main", BindingFlags.Default | BindingFlags.InvokeMethod, null, null, null);
Community
  • 1
  • 1
shybovycha
  • 11,556
  • 6
  • 52
  • 82

3 Answers3

4

Why are you craeting an instance (Activator.CreateInstance(t)) in order to invoke a static method???? It should be:

t.InvokeMember(
     "Main", 
     BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, 
     null, 
     null, 
     new object[] { new string[0] }
);

Also this method as defined doesn't return any value so no need to assign it a return variable.

And in order to clear all misunderstandings I've created a full working demo here: http://www.mediafire.com/?n7h9b8ghomfv17d

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • Not static, nor usual one haven't give any results. The same exception. So with your code. – shybovycha Jan 16 '11 at 21:28
  • Additionally, note that the asker wasn't supplying any arguments for the method. – Anon. Jan 16 '11 at 21:29
  • @shybovycha, from what I can see `Class1.Main` is a static method. Also did you notice the last argument I am passing to the `InvokeMember` call: `new object[] { new string[0] }` instead of `null`. – Darin Dimitrov Jan 16 '11 at 21:29
  • Just as i said, there is no care if i try to invoke method with or without arguments; if it's static one or no - the result keeps same - exception is rising. – shybovycha Jan 16 '11 at 21:32
  • @shybovycha, when I tested it worked fine. There must be something different in your code that you haven't shown. – Darin Dimitrov Jan 16 '11 at 21:32
  • 1
    @shybovycha: If the exception still happens even when you do it properly, why didn't you post the "correct" code in your original question? As it is, we don't know whether you've actually tried it properly, or whether you just think you've tried it the "correct" way but had some other bug you missed. – Anon. Jan 16 '11 at 21:34
  • @shybovycha, here's a working proof of concept demo I've created for you: http://www.mediafire.com/?n7h9b8ghomfv17d – Darin Dimitrov Jan 16 '11 at 21:42
4

You are likely getting the InvokeMember() arguments wrong. Here's a sample that works:

using System;
using System.Reflection;

class Program {
    static void Main(string[] args) {
        if (args.Length > 0) Console.WriteLine(args[0]);
        else {
            Assembly asm = Assembly.LoadFrom(Assembly.GetEntryAssembly().Location);
            foreach (Type t in asm.GetTypes()) {
                if (t.IsClass == true && t.FullName.EndsWith(".Program")) {
                    //object obj = Activator.CreateInstance(t);
                    object res = t.InvokeMember("Main",
                        BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.InvokeMethod,
                        null, null,
                        new object[] { new string[] { "Invoked" } });
                }
            }
        }
    }
}
  • Note how the Main() method isn't public, thus BindingFlags.NonPublic
  • Note how the Main() method is static, thus BindingFlags.Static
  • For the same reason, pass null for the target parameter
  • For the same reason, CreateInstance isn't necessary
  • Note how the Main() method takes a string[] argument, you have to pass it to get Reflection to find the correct method overload.

Follow the same logic for your Main() method:

                object res = t.InvokeMember("Main",
                    BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod,
                    null, obj,
                    new object[] { });
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • Yeah, thanks! I was wrong while passing string array as argument list (see InvokeMember()). Thanks a lot! =) – shybovycha Jan 16 '11 at 21:47
2

The last parameter to InvokeMember is an array of Objects containing the arguments to the method.

You're passing null there, you should be passing an array of objects containing a single element (an array of strings).

Anon.
  • 58,739
  • 8
  • 81
  • 86