Well using this awesome blog, i noticed that it can easily handle extension from other coders. Well this lead me to thinking of building a Windows/Console Proof of Concept (PoC) project. The goal was to see how to engineer a program which can use plug-ins (developed by others) and instantly use them without the need to recompile the code. Thus a truly pluggable program. Well here is the good news, its easier than I had thought to do it with .Net, so lets start.

My PoC is split into 3 projects

  1. interfaceLib (class lib) - this stores all the public interfaces supported by the application.
  2. Plugin (Class lib) - a very simple plug in (Hello world, what else)
  3. PlugControlApp (Win app) - this is the main application, which will be able to load in the Plug in and use it.

Why do i have the interface as a different project?.
1. allows you to only share the interface code
2. allows developers to save/reference only the interfaceLib project, and be able to build and compile their plug-in
3. keeps things a little simpler (well it did in this example.)

1. interfaceLib  (Design by Interface)

If you have used Design Patterns or the Enterprise Library, you may note the increase in Design by interface (hopefully you are very used to this idea). if your note i really would recommend googling this (I may write an example app at a later date to demo this concept). ANYWAY going off track....

In my example I want to be able to

  • Find what version of my application the plug is written for
  • Find the Name of the plug in
  • Have a DoSomthing function which takes in a string and returns a string.

Here is the class Diagram:

I decided to split them into 2 different interfaces as the Plugin interface would be useful for all my plug-ins, and the DoSomthing interface seemed more specialized.

2. Plugin - The outside world can help! YEAH!

This project can reference the interfaceLib, allowing the developer to build and compile a plug in control, with out the need to reference the actual application. For the plug in all i really wanted to do is to have a Hello world.... so to accomplish this the plug-in will need to follow these steps

  • Remember to add a reference to the interfaceLib and using statement

using interfaceLib;

  • In your class inherit off the required interfaces (DoSomthing in this case)

/// <summary>
/// Simple hello world plug-in!
/// </summary>
public class WriteHello : DoSomething
{

  • implement the methods to satisfy the contract of the interface

/// <summary>
/// using the DoSomthing interface, which the application supports.
/// </summary>
/// <param name="param">your name</param>
/// <returns>Hello 'your name'</returns>
public string DoSomthing(string param)
{
 return "Hello " + param;
}

  • Compile it

There it is, a plug in

3. PlugControlApp - Your uber cool application

Finally your application will also use 'design by interface' to implement the various plug-ins. I have built a small rig for this, which in this article i will only highlight the main points. As you application will handle things differently, and also i will include the projects zipped up for you to download.

This project will require the following using imports

using System.Reflection;
using interfaceLib; //this will describe the plug-in interface

The main routines were to load the plug in, and then use it. So lets review these

A. Loading in a plug-in (there are many different ways to do this), in my example project i store the loaded list of plug-ins in a List<Type>, and then store this in a listbox. (ListBox1)

/// <summary>
/// This will load in all the plug-in class, which
/// this application can support.
/// </summary>
/// <param name="location">The full location of the plug in.</param>
/// <returns>False, if it cannot load in any plug-in</returns>
private bool LoadPlugin(string location)
{
 bool rValue = false;
 try
 {
  Assembly a = Assembly.LoadFile(location);
  //now get all avalible publlic types.
  foreach (Type t in a.GetTypes())
  {
   //only interested in plublic classes.
   if (t.IsPublic && t.IsClass)
   {
    //does our application support the plug-in?
    if (IsSupportedInterface(t))
    {
     plugins.Add(t); //add this to the loaded plugins.
     rValue = true; //was able to load in a plug-in.
    }
   }
  }
 }
 catch (Exception ex)
 {
  //do nothing, could not load in
  //the dll, should have some logging code
  //here.
 }
 return rValue;
}

Remember you only want to be able to load in plug-ins which support your interfaces. (my load in function uses the following to do this).

/// <summary>
/// simple method to see if the plug as a interface which is supported.
/// </summary>
/// <param name="_plugin">plug in type. (class)</param>
/// <returns>True if this class can be supported.</returns>
private static bool IsSupportedInterface(Type _plugin)
{
 Assembly a = Assembly.GetAssembly(typeof (Plugin));

 foreach (Type t in a.GetTypes())
 {
  foreach (Type iType in _plugin.GetInterfaces())
  {
   if (t.Name == iType.Name)
    return true;
  }
 }
 return false;
}

B. Now as we loaded in the plug-in, you can now invoke the new way of processing the Dosomething method

firstly what I did here was to gain a reference to the class type,

//the actual class need to be referenced
Type ob = (Type) listBox1.SelectedItem;

this will allow us to create an instance of the class, using this method to create the object

/// <summary>
/// Create an instance of the object
/// </summary>
/// <typeparam name="T">Type of the object to create</typeparam>
/// <param name="_plugIn">The class as a type.</param>
/// <returns>a created instance of this class.</returns>
private static T createInsatance<T>(Type _plugIn) where T : class
{
 return (T) Activator.CreateInstance(_plugIn);
}

the following code uses some functions to create the instance (above) of the required object and then it runs the method. note that the interface is being used to do this

//note the interface
DoSomething p = createInsatance<DoSomething>(ob);
//get the user input from a textbox
TextBox text = GetControlByName<TextBox>("userInput");
if (text != null)
{
 //program against the plug-in using the
 //interface
 MessageBox.Show(p.DoSomthing(text.Text), p.Name);
}

So what does this all give you??

 

 

Files:

interfaceLib.zip (9.38 kb)

PlugControlApp.rar (39.26 kb)

Plugin.rar (11.16 kb)

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Related posts

Comments are closed