This example shows how you can dynamically load assemblies in .NET applications. For instance to support plugins in your application.

(If you want to skip the explanation and download straight away you can go here)

Introduction

In .NET every dll you make a design time reference too, is linked and loaded automatically at runtime. The end-user can customize this a bit using .config files, but that is tedious work at best. The problem is if you only know what dlls to load at run-time. For instance you want to load all the dlls that are located in a special folder (e.g. MyApp\Plugins) but you want allow 3rd party's to create dlls for your  application as well.

You can create old fashioned dlls that are just static library's, but that has a lot of drawbacks, the most important are:

  •  You can't create one using C# or VB.
  •  Not managed.
  •  Not typesafe (no type checking possible by the .NET runtime).
  •  plain old ugly :-)

In good old fashioned COM it was easy: you could just CoCreateInstance every plugin and use the interfaces that it has.

Luckily, there is something similar in the .NET framework. The trick is, you can use the System.Reflection.Assembly object to load dlls that are assemblies and query it for information.

Typelibrary in .NET? Yep

Before digging into the assembly, there is a second important question:
-How does the program know what to look for?
It didn't know the plugin at compile time so it has absolutely no idea about what classes to look for. Most likely the assembly will use completely different namespaces.
Here we can use a concept from COM: the typelibrary. Off course .NET doesn't have real typelibrary files but we can make something similar. All you have to do is create a separate class library that just contains interfaces that you will use. That will make it a pretty small library, but because it contains all the interface, 3rd party's can link to it and use the interfaces. Your own program can also refer to this class library and will also know the interfaces.
(Note: be very careful with changing interfaces after you released the class library. Doing this will send you straight back to DLL-hell.)

OK, we got the interfaces that have to be used by both the plugin, and the program that will use the plugins, now HOW CAN I CREATE THEM.

Loading assemblies at runtime

It just takes a few steps. The first step is to load an assembly at run time by using:

System.Reflection.Assembly myAssembly = System.Reflection.Assembly.LoadFile(fileName);

That will load the assembly. But we can't use it just yet. First we have to find an object that can be created. This is quite simply done by using:

Type[] pluginTypes = myAssembly.GetTypes();

This gets you a list of all the types in the assembly. All kinds of objects you can create. But we don't want 'all kinds of objects', we want an object that supports a specific interface (e.g. you specify the ICar interface, but also have a IDoor, IWheel and IWindow interface. You only want to create a car, the door, wheel and window are aggregated by it). If you have more than one toplevel interface in your design, it might be usefull to add a factory interface that allows creation of the toplevel objects. This will allow only a single object with the toplevel interface, making things a bit more clear. So you would have a IMyFactory with the methods CreateCar and CreateAccident.

This means we have to search the types for an object that supports a specific interface. Once again this can be done easily. We just have to check each type and see if it has the interface:

foreach (Type tpePlugin in pluginTypes)
{
System.Type myInterface = tpePlugin.GetInterface("MyPluginDefinitions.IMySpecialPlugin");
if (null != myInterface )
{
... // code to load the plugin
}
}

The GetInterface method returns a valid System.Type object if the specified interface is supported. If the interface isn't supported, null is returned. You can also get the name of the interface through System.Type information instead of typing it yourself like I did.

If the interface we want is supported by this type we finally found the object type that has to be instantiated. (At this moment we only looked into the metadata to look for an object that supported the interface we need).

Creation is quite simple:

object objPlugin = myAssembly.CreateInstance(tpePlugin.FullName);
//Cast it to the interface that we do know it supports : IMySpecialPlugin
MyPluginDefinitions.IMySpecialPlugin myPlugin = objPlugin as MyPluginDefinitions.IMySpecialPlugin;

By using the CreateInstance method we can finally create the instance of an object. We have to specify the object (and NOT the interface) so we go back to the tpePlugin System.Type object to get the fullname of the object that supports the interface. After creating the object it can be casted to the interface defined in the class library. Now you got a run-time loaded assembly with a known interface with all the advantages.

Example application

I got an example application (with source code) that uses this mechanism. It has one .exe and three class libraries. The plugin1 and plugin2 projects are plugins (there is a surprise). The PluginDefinition is the 'typelibrary' that contains the interface that has to be used. The example shows only a single interface, but you can easily add more interfaces to the 'typelibrary' project.

The program loads a form. In the Form_Load all the dlls in the same folder are analyzed to see if they support the interface the program wants. Every dll that supports the interface is loaded. After the dlls are loaded a listbox is filled with the shortdescription of each of the plugins. The main window has two more buttons: description and show. Description shows a messagebox containing the longdescription of the plugin, show lets the plugin show a dialog. Plugin1 has a dialog, plugin2 shows a messabox.

Off course the Scrolling LED Gif Generator also uses this same mechanism (yes, I'm eating my own dog food ;) )

Final notes before the download:

Written in C#.Net with Visual Studio .Net 2003. I don't know if it can be opened in a previous version of Visual Studio. But the code is pretty straight forward so it shouldn't be hard to understand.
Be careful though. There is no error handling so if you try to load a dll that is not an assembly, the application will crash and burn. (You should be able to figure out how to handle System.BadImageFormatException errors :-) ).

Finally

Now here is the download. (only about 22 kb)

Unzip it and load the solution file (.sln). Build the whole solution and run the PluginConceptTest program. The class libraries already put there (debug) output in the same folder as the PluginConceptTest program itself.

If you have any questions, you can always mail me