106

I am trying to figure out how you could go about importing and using a .dll at runtime inside a C# application. Using Assembly.LoadFile() I have managed to get my program to load the dll (this part is definitely working as I am able to get the name of the class with ToString()), however I am unable to use the 'Output' method from inside my console application. I am compiling the .dll then moving it into my console's project. Is there an extra step between CreateInstance and then being able to use the methods?

This is the class in my DLL:

namespace DLL
{
    using System;

    public class Class1
    {
        public void Output(string s)
        {
            Console.WriteLine(s);
        }
    }
}

and here is the application I want to load the DLL

namespace ConsoleApplication1
{
    using System;
    using System.Reflection;

    class Program
    {
        static void Main(string[] args)
        {
            var DLL = Assembly.LoadFile(@"C:\visual studio 2012\Projects\ConsoleApplication1\ConsoleApplication1\DLL.dll");

            foreach(Type type in DLL.GetExportedTypes())
            {
                var c = Activator.CreateInstance(type);
                c.Output(@"Hello");
            }

            Console.ReadLine();
        }
    }
}
danbroooks
  • 2,712
  • 5
  • 21
  • 43
  • http://stackoverflow.com/questions/2202381/reflection-how-to-invoke-method-with-parameters – Stu Aug 21 '13 at 16:08

7 Answers7

147

Members must be resolvable at compile time to be called directly from C#. Otherwise you must use reflection or dynamic objects.

Reflection

namespace ConsoleApplication1
{
    using System;
    using System.Reflection;

    class Program
    {
        static void Main(string[] args)
        {
            var DLL = Assembly.LoadFile(@"C:\visual studio 2012\Projects\ConsoleApplication1\ConsoleApplication1\DLL.dll");

            foreach(Type type in DLL.GetExportedTypes())
            {
                var c = Activator.CreateInstance(type);
                type.InvokeMember("Output", BindingFlags.InvokeMethod, null, c, new object[] {@"Hello"});
            }

            Console.ReadLine();
        }
    }
}

Dynamic (.NET 4.0)

namespace ConsoleApplication1
{
    using System;
    using System.Reflection;

    class Program
    {
        static void Main(string[] args)
        {
            var DLL = Assembly.LoadFile(@"C:\visual studio 2012\Projects\ConsoleApplication1\ConsoleApplication1\DLL.dll");

            foreach(Type type in DLL.GetExportedTypes())
            {
                dynamic c = Activator.CreateInstance(type);
                c.Output(@"Hello");
            }

            Console.ReadLine();
        }
    }
}
Dark Falcon
  • 43,592
  • 5
  • 83
  • 98
  • 13
    Note that this tries to call `Output` on every type in the assembly, which will likely throw before the "right" class is found... – Reed Copsey Aug 21 '13 at 16:08
  • 1
    @ReedCopsey, Agreed, but for his simple example, his type is the only one visible. "The only types visible outside an assembly are public types and public types nested within other public types." For a non-trivial example, obviously this will be an issue... – Dark Falcon Aug 21 '13 at 16:13
  • 1
    Neat with the two examples! :) – Niels Abildgaard Mar 06 '14 at 22:16
  • 28
    This is why interfaces are often used and you can do feature detection such as `IDog dog = someInstance as IDog;` and test if it is not null. Put your interfaces in a common DLL shared by clients, and any plugin that will be loaded dynamically must implement that interface. This will then let you code your client against the IDog interface and have intellisense+strong type checking at compile time rather than use dynamic. – AaronLS Nov 09 '14 at 20:38
  • you may put c.Output in a try catch block and when you catch the exception `continue` so that as @ReedCopsey said this doesn't throw before the class is found. – Alan Deep Apr 14 '18 at 04:14
  • 1
    For the first note, you can use 'object' instead of 'dynamic', and test the type before using it, like: \r\n object obj = Activator.CreateInstance(type); \r\n if (obj is Class1) { obj.Output("Hello1"); } – Tarek.Mh Jan 23 '19 at 11:55
  • 2
    @Tarek.Mh: That would require a compile-time dependency on `Class1`. At that point you can just use `new Class1()`. The asker explicitly specified a runtime dependency. `dynamic` allows the program to not require a compile-time dependency on `Class1` at all. – Dark Falcon Jan 23 '19 at 13:41
48

Right now, you're creating an instance of every type defined in the assembly. You only need to create a single instance of Class1 in order to call the method:

class Program
{
    static void Main(string[] args)
    {
        var DLL = Assembly.LoadFile(@"C:\visual studio 2012\Projects\ConsoleApplication1\ConsoleApplication1\DLL.dll");

        var theType = DLL.GetType("DLL.Class1");
        var c = Activator.CreateInstance(theType);
        var method = theType.GetMethod("Output");
        method.Invoke(c, new object[]{@"Hello"});

        Console.ReadLine();
    }
}
Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
24

You need to create an instance of the type that expose the Output method:

static void Main(string[] args)
    {
        var DLL = Assembly.LoadFile(@"C:\visual studio 2012\Projects\ConsoleApplication1\ConsoleApplication1\DLL.dll");

        var class1Type = DLL.GetType("DLL.Class1");

        //Now you can use reflection or dynamic to call the method. I will show you the dynamic way

        dynamic c = Activator.CreateInstance(class1Type);
        c.Output(@"Hello");

        Console.ReadLine();
     }
Alberto
  • 15,626
  • 9
  • 43
  • 56
  • Thank you so much - this is just what I am looking for. I can't believe this isn't higher rated than the other answers, since it shows the use of the dynamic keyword. – skiphoppy Mar 24 '16 at 16:47
  • Ah, now I see it was in DarkFalcon's answer as well. Yours was shorter and made it easier to see, though. :) – skiphoppy Mar 24 '16 at 16:51
0

Activator.CreateInstance() returns an object, which doesn't have an Output method.

It looks like you come from dynamic programming languages? C# is definetly not that, and what you are trying to do will be difficult.

Since you are loading a specific dll from a specific location, maybe you just want to add it as a reference to your console application?

If you absolutely want to load the assembly via Assembly.Load, you will have to go via reflection to call any members on c

Something like type.GetMethod("Output").Invoke(c, null); should do it.

Fredrik
  • 97
  • 7
0
foreach (var f in Directory.GetFiles(".", "*.dll"))
            Assembly.LoadFrom(f);

That loads all the DLLs present in your executable's folder.

In my case I was trying to use Reflection to find all subclasses of a class, even in other DLLs. This worked, but I'm not sure if it's the best way to do it.

EDIT: I timed it, and it only seems to load them the first time.

Stopwatch stopwatch = new Stopwatch();
for (int i = 0; i < 4; i++)
{
    stopwatch.Restart();
    foreach (var f in Directory.GetFiles(".", "*.dll"))
        Assembly.LoadFrom(f);
    stopwatch.Stop();
    Console.WriteLine(stopwatch.ElapsedMilliseconds);
}

Output: 34 0 0 0

So one could potentially run that code before any Reflection searches just in case.

0

A few years later...

Below is what worked for me, for getting a property value from a DLL. In the GetType() method call, I had to use namespace.classname.


Assembly dllAsm = Assembly.LoadFile(@"C:\THE\FULL\PATH\TO\YOUR\DLL\yourdynolib.dll");

Type yourType = dllAsm.GetType("YourNamespace.YourClassName");
var yourInstance = Activator.CreateInstance(yourType);

PropertyInfo piYourType = null;
piYourType = yourType.GetProperty("YourPropName");

string yourPropValue = (string)piYourType.GetValue(yourInstance);
-2

It's not so difficult.

You can inspect the available functions of the loaded object, and if you find the one you're looking for by name, then snoop its expected parms, if any. If it's the call you're trying to find, then call it using the MethodInfo object's Invoke method.

Another option is to simply build your external objects to an interface, and cast the loaded object to that interface. If successful, call the function natively.

This is pretty simple stuff.

ChrisH
  • 975
  • 12
  • 21
  • Wow, not sure why the down-votes. I have a production application doing exactly this for like the past 12 years. * shrug * Anyone needs some code to do this, shoot me a message. I'll package up portions of my production code and send it along. – ChrisH Sep 23 '16 at 11:26
  • 16
    I suspect the downvotes would have to do with the lack of examples and condensing tone... Seems like you have the basis for a full answer though, so don't be afraid to edit in more details :) – Shadow Aug 02 '18 at 05:29
  • 6
    It's just kind of rude to say "this is pretty simple stuff", and that's why got you the downvotes. – ABPerson May 29 '20 at 22:43
  • 1
    I wasn't being rude or condescending.... 6 years ago. Tone doesn't come through in text, clearly. It was really meant to be pretty light hearted... I also *really* feel like I had a link to a code sample in there all those years, and I have no idea where it went (assuming it really was there like I'm remembering). :\ – ChrisH May 31 '20 at 01:08
  • I don't know how MethodInfo works but it seems valuable. I'd say your answer has the potential to be better than the current accepted one but it would need to be completed. If you ever get around to it it would be appreciated. If so, please don't link to a code sample. These may get broken in the future. It is best to provide the sample yourself, with possibly a link to a source or extra info for continued reading. – SpaghettiCook Jul 15 '20 at 11:06