Introduction:

First things first: I wrote this article mainly because I’ve found a complete lack of comprehensive information on the subject. Sure there are projects out there that show you how to make a simple plug-in that lets you do something dumb like feed it two numbers, and then one plug-in may add the two numbers together and return the result, while the other plug-in may multiply them instead. This is where I began, and is not bad reading as a prep tutorial to this tutorial. It might not be a bad idea to understand those tutorials first. However, when I began to search for how to bring GUIs into the plug-ins, I was completely lost. My aim is to show you how to do this, and provide some clear explanations. This tutorial will not be a step by step guide to making the program, as if you have the tutorial, you should also have the source code for the Tutorial project. I feel it unnecessary to go through this all, so instead I will only be covering some explanations on the code that is tricky.

Tutorial:

Notice that we have a Solution with 6 different projects in it. The first project is the Plugin Application Tester thingy. I like to refer to it as the Host, since it is the application that your plug-ins will be plugging-in to. This of course is the heart and soul of the whole Plug-in get-up. The plug-ins themselves are really not that complex as you will see.

But before I go into the Host Application, let’s talk about the PluginInterface project. If you are unfamiliar with interfaces, please go find a tutorial and familiarize yourself with them. In a nutshell, they are ‘outlines’ of what properties and methods a Class that inherits a certain interface should have in them. There is very little code in this project. In fact, the only code we’re worried about is this:

In this code, we have 2 Interfaces declared. These are the legends, the roadmaps, the keys to what a class inheriting these interfaces should do. For example, if we were to declare a new class in code and have it inherit IPlugin interface, the class would have to employ four readonly string properties (Name, Description, Author, Version), an IPluginHost type property, a UserControl property, and two voids: one called Initialize, the other called Dispose. Only if a class has all of that as said above, can it legally inherit the IPlugin Interface. This of course makes sense if you think about it. If we’re going to make plug-ins, we need establish some ground rules. Just think of what would happen if we tried calling a method in a plug-in that didn’t exist! This way, we establish a set of guidelines that every plug-in must follow, and we can predict just what the plug-in is going to be capable of doing. When all is said and done, we have 2 interfaces: IPlugin and IPluginHost. It should be clear that IPlugin is the interface that all Plug-ins will have to inherit. However, we also will make our Plug-in host inherit the IPluginHost interface. This way, we can send the Plug-in a reference to the host later on. This will allow the Plug-in some access to things of its host. When this project is compiled, it produces PluginInterface.dll file. We made this in its very own DLL for a reason. Now we have a common roadmap to share between plug-ins and the plug-in host that contains definitions for each object.

Next up, we have the Host project. This one is a bit more confusing. First of all, let’s get this cleared out of the way. I used a technique that may be a bit confusing at first for some, but once scaled up, can make things a bit simpler later on.

The above code is a trick I learned a little while ago. I have a class called Global. Inside this class is a static public object. This object in this case happens to hold an instance of the PluginServices object. All that this little trick does is makes sort of a global instance of the (PluginServices) object that can easily be accessed from the entire application. This is instead of declaring an object and making a new instance in the Main form of the host application. Now, I can access the instance of the PluginServices object anywhere in the program by typing Global.Plugins.! It’s that simple!

The next bit of code I will introduce is the code that actually finds any compatible plug-in files in a certain folder, and adds them to a collection of AvailablePlugins:

This is a very simple method that accepts a path to look for plug-ins in. For example, you could tell it to look in your application’s Plugins folder. All it does is loop through all the files in the given directory, and check to see if the extension is of .dll type. Now you may be wondering if that’s enough to check for. What if someone renamed some random file as .dll and stuck it in? We’ll get to that later.

You may have noticed in the last method, a call to another method AddPlugin(). I’m not going to paste the whole function in here, as that would be entirely redundant, but I will explain what’s going on in a few spots. First of all, we’re declaring a new Assembly type, and loading the passed filename into it:

Next, we’re iterating through all the different types that the particular plug-in contains. It would be wise to note at this time, that you may want to put in some extra error catching around this area. I would imagine if you tried loading an Assembly that isn’t a proper assembly, and getting its types, you may run into some problems. But we’ll leave that for you to play with. The next few if statements are just checking some attributes of the given type found in the assembly. We want to make sure it’s public and accessible to us, as well as not an abstract class. Here is the next chunk of code:

//Gets a type object of the interface we need the plugins to match
Type typeInterface = pluginType.GetInterface("PluginInterface.IPlugin", true);
//Make sure the interface we want to use actually exists
if (typeInterface != null)
{
//Create a new available plugin since the type implements the
//IPlugin interface
Types.AvailablePlugin newPlugin = new Types.AvailablePlugin();
//Set the filename where we found it
newPlugin.AssemblyPath = FileName;
//Create a new instance and store the instance
//in the collection for later use
//We could change this later on to not load an instance..
//we have 2 options
//1- Make one instance, and use it whenever we need it..
// it's always there
//2- Don't make an instance, and instead make an instance
//whenever we use it, then close it
//For now we'll just make an instance of all the plugins
newPlugin.Instance = (IPlugin)
Activator.CreateInstance(pluginAssembly.GetType(pluginType.ToString()));
//Set the Plugin's host to this class which inherited IPluginHost
newPlugin.Instance.Host = this;
//Call the initialization sub of the plugin
newPlugin.Instance.Initialize();
//Add the new plugin to our collection here
this.colAvailablePlugins.Add(newPlugin);
}

This bit of code is the heart of the whole plug-in project. First, we declare a new Type object, and make it the type of our IPlugin interface. Notice here, we’re calling the GetInterface() method. What this does is tries to sort of cast the Type that we are on in the for loop to the IPlugin interface. If the Type does actually implement the IPlugin interface, typeInterface will not be null. That’s really the hardest step. Next, we’re just making a new instance of the class we created ‘AvailablePlugin’. We set the AssemblyPath property. On the next line, we’re doing something else. The AvailablePlugin type has a property called Instance. This is where we will be storing the actual instance of the loaded plug-in, ready to use. Now, for the purpose of this tutorial, I tried to keep things easier. I decided that my program would simply load an instance of every plug-in it finds and have it ready for use, regardless of whether or not the plug-in will actually be used. There is another way to do this as well. What if you don’t want to use all the plug-ins? It’s a bit of a waste of memory to load them all up if you’re not going to use them all. You could implement some sort of Loading and Unloading of plug-ins if you choose to go this route. Like I said though, that is beyond the scope of this article.

After this is done, we can use the new instance of the plug-in, and set its Host property. Since the class that we are working in happens to inherit the IPluginHost interface, we can conveniently set the Host property to this. Finally, we call the Initialize() method of the plug-in to alert it to do anything it needs to start, and then we add it to the collection of AvailablePlugins.

The only other bit of code I think we should cover is the FormFeedback() function implemented by the PluginServices class:

publicvoid Feedback(string Feedback, IPlugin Plugin)
{
//This sub makes a new feedback form and fills it out
//With the appropriate information
//This method can be called from the actual plugin with its Host Property
System.Windows.Forms.Form newForm = null;
frmFeedback newFeedbackForm = new frmFeedback();
//Here we set the frmFeedback's properties that i made custom
newFeedbackForm.PluginAuthor = "By: " + Plugin.Author;
newFeedbackForm.PluginDesc = Plugin.Description;
newFeedbackForm.PluginName = Plugin.Name;
newFeedbackForm.PluginVersion = Plugin.Version;
newFeedbackForm.Feedback = Feedback;
//We also made a Form object to hold the frmFeedback instance
//If we were to declare if not as frmFeedback object at first,
//We wouldn't have access to the properties we need on it
newForm = newFeedbackForm;
newForm.ShowDialog();
newFeedbackForm = null;
newForm = null;
}

This is really simple actually. The whole idea of the IPluginHost interface is to allow some communication between the main application that runs the plug-ins, and the plug-ins themselves. In this example, we didn’t take advantage of this idea to a great extent, however in your own applications, this idea can become extremely powerful. Think about it. You could expose a lot of functionality of your main program. For example, let's say you were making an MP3 player, you could make the IPluginHost interface have methods such as PlayMp3(), Stop(), Previous(), Next(), SetVolume(), etc. This would allow your plug-ins to take some control over the main program. Hopefully, this idea makes sense to you. In our example here, all we’ve done is incorporated a method that plug-ins can call which displays a new form with a string that the plug-in passed along, as well as some information on the plug-in that called the method.

OK, so we talked about the actual host application for the plug-ins, but we have yet to talk about the plug-ins themselves. The plug-ins themselves are not that complicated. All you have to do to make one is create a new Class Library project, and make a class that inherits the IPlugin interface. Remember, you will need to add a reference to the project to the PluginInterfaces project that our interfaces reside in. Also, another tip: when you add the reference, make sure the CopyLocal property of the reference is set to false! Otherwise, you will have some unexplained problems. The only place where the CopyLocal property should be true is on the Host application’s reference to the PluginInterfaces project. This really makes sense if you think about it, since the Host will always have the PluginInterfaces.Dll file. The plug-ins just use it from the Host program.

I’ll let you take a look at the actual plug-ins yourself. Plug-ins 1 through 3 are all made pretty much the same. They all have a Class which inherits the IPlugin interface and implements all its methods and properties. The plug-ins also contain a UserControl class in them. This is the GUI for the plug-in. It is exposed as a property in the IPlugin interface (MainInterface). This allows the host to add the UserControl to a panel or something to expose a GUI to the plug-in.

Notice that in Plugin3, in the UserControl itself, notice we have properties of IPlugin and IPluginHost types. This allows the UserControl access to the Plug-in Host’s methods (in this case, ShowFeedback(), which is the whole point of this plug-in), and the Plug-in’s methods itself.

Finally, in Plugin4, we demonstrate that you don’t need to have a separate Plugin class and UserControl class for a plug-in. We created a UserControl that also inherits the IPlugin interface. We’ve combined two classes into one.

Conclusion:

And that’s about it for plug-ins. Once you’ve grasped the concept of Interfaces, and have understood the code for actually initializing instances of plug-ins, creating an actual plug-in is not a very difficult task. I will leave you to explore the rest of the code by yourself. Hopefully you have learned something from this tutorial, and can successfully implement plug-ins into your own programs!

History

February 26th, 2004 - Submitted article.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

Comments and Discussions

Love this code, and have been using it in an automation project that I am working in conjunction with several others. Using your example below, I have made it possible for us to create Custom Usercontrols for our GUI that are dynamically added as a plugin!

Here is my issue, if we upgrade the plugin, we can not re-install it. Is there a way for it to be in it's own appDomian so we can close that domain, reinstall the plugin with the new version?

EDIT: I figured this out, so I am going to answer my own question here!

YES - There is a way to do this!
It is such a simple answer, I can't believe so many people (Including me) did not see this!
It can all be done by changing 1 line of code!
in your code, find the following line:

Assembly pluginAssembly = Assembly.Loadfrom(FileName);

and change it to this:

Assembly pluginAssembly = Assembly.Load(File.ReadAllBytes(FileName));

This actually loads the "Contents" of the assembly file, instead of the file itself. Which means there is NOT a file lock placed on the assembly file! So now it can be copied over, deleted or upgraded without closing your application or trying to use a separate AppDomain or Marshaling!

PROS: Very Simple to fix with a 1 Liner of code!CONS: Cannot use AppDomain, Assembly.Location or Assembly.CodeBase.

NOTE: Also, in your code for upgrading, you must uninstall the old one first and be sure to get rid of any instances you may have created and to empty the AvailablePlugins before you upgrade with the new dll!

Thank you for this well documented piece of code. I like it very much also because the used programming style I also always try to use.I've had a look to the newer System.Addin and MEF techniques, but they ara such a brain blasting incomprehensible frameworks. This one is clear to understand. I hate to add code to my projects i don't understand. Maybe you will find time to convert this sometimes to WPF and add eventhandling.

I wanted to thank you because you really got me out of a real mess ! Thank you again.

I understood most of what your code was doing, but I didn't really get the part on the IHost interface. Maybe that's because my plugins don't need to change the interface of the host application or something like that.

1. Consider a case if dll are loaded from different project but inside a single solution. then how to hide current instance dll and load new dll2. if the new dll is closed the old dll should be shown with the same state?

//Create a new instance and store the instance in the collection for later use
//We could change this later on to not load an instance.. we have 2 options
//1- Make one instance, and use it whenever we need it.. it's always there
//2- Don't make an instance, and instead make an instance whenever we use it, then close it
//For now we'll just make an instance of all the plugins

I can't figure out, how 2 will be done.

I changed from Usercontrols to MDI-Forms in your example and need an example how to create / show these forms by clicking e.g. a button when I need them.

In the PluginInterface I added a usercontrol with a panel and a label on it to create a consistent look in my plugins.

All usercontrols in my Plugins inherit from this usercontrol. Problem is, that newly added controls to this inherited usercontrol are not shown when running the program - I just see my original usercontrol from the PluginInterface.

I guess there will be a problem with the constructur, but I don't get it.

Has anyone successfully implemented this design with creating the plugins in a separate appdomain?I've already implemented this plugin framework into my application, so I'd like to be able to do the app domains with minimal changes, if that is possible.

Having to rework the system which i've already implemented will definitely be a pain....

Nice article There are several describing a similar setup, and this seems the best. I think the division of the interfaces should have more attention though :

The plugin interface:This is for host-initiated communication, but can be used to "push" to the plugin, or to "pull" from the plugin.

The host interface:This is for plugin-initiated communication. Again, this can be push or pull.

Also that you can split up these into more than one interface if things get large.

I'm using something similar myself here, and have had a few problems :

1) XmlSerialization from the host, with a type defined in a plugin, does not work unless "extra types" are supplied, which can lead to passing of the "extra type" info back and forwards over the interfaces.

2) I've had problems debugging the plugin code. Having a separate project for the plugins, and using the debug option "run using external application", and naming the host as the app, sometimes fixes this. I haven't tied this issue down entirely, as it's confused for me by threading issues. I think it's down to the plugin code not being statically linked to the host, but loaded through assembly.load().

3) I've also had problems with static class constructors seemingly not being called, not entirely sure about this either- having put a workaround in place, I've moved on.

I'd be interested if anyone else has experienced these problems, and what workarounds they may have found.

The plugin is called by a form and pass a parameter. In the plugin I use a background worker to do some actions, and then I need to send the result back to the form but I don't know how to access to this particular form ?

I've been trying to find out how to make plugin-able apps in .NET for weeks now. Apparently, all I was missing was the Reflection part in my application. I must say this is a great article for beginners like us to get started.

But I have a question regarding this article. Since this article is about the version 1.1 of the framework, is there an improved way to do these things in 3.5?

Hi,Very nice article...It compile and run very well, as espected.To realy understand all of it, i wrote my own project, doing the same as your.

I have an exception in the folowing linenewPlugin.Instance = (IPlugin)Activator.CreateInstance(pluginAssembly.GetType(pluginType.ToString()))

The exception is about : unable to cast from MyPlugIn to IPlugIn

My PlugIn class derives from IPlugIn...All Namespaces seems to be good...I tried a very simple other project, it's the same.I tried with something like :IPlugIn xxx=AppDomain.CurrentDomain.CreateInstanceFromAndUnwrap("MyPlugin.dll", "ns.PlugIn");It's the same...I can't cast!

"Remember, you will need to add a reference to the project to the PluginInterfaces project that our interfaces reside in. Also, another tip: when you add the reference, make sure the CopyLocal property of the reference is set to false! Otherwise, you will have some unexplained problems. The only place where the CopyLocal property should be true is on the Host application’s reference to the PluginInterfaces project. This really makes sense if you think about it, since the Host will always have the PluginInterfaces.Dll file. The plug-ins just use it from the Host program."

You probably have the reference to the assembly containing the plugin/host interfaces set to CopyLocal on both the Host project and the plugin project, in which case you'd end up with two copies of the assembly with the plugin/host interfaces. If this happens, the Host uses its own, and the Plugin uses its own and it thinks the interfaces are not the same.

You need your plugins and host to share the same interface in the SAME assembly. Even though you could make the same interface in both assemblies, it wouldn't be the 'same' one. You could I suppose, include PluginInterface.cs in your host and then reference the host's assembly directly in your plugin, but you must reference the interface from the same assemby in both the host and plugin.