Click here to Skip to main content
15,879,096 members
Articles / Programming Languages / C#
Article

Enabling Your Application to Become a Plugin Host

Rate me:
Please Sign up or sign in to vote.
3.56/5 (33 votes)
3 Jun 2006CPOL5 min read 85K   986   77   17
This brief article is a compilation of the .NET mechanisms that allow for discovering and late-binding external code to your application.

Introduction

Your application may gain a lot of flexibility if you allow yourself and others to add new functionality after deployment. This can be done if you enable your code to host new components that are not yet known at design time, sometimes referred to as Plugins.

In this article, you learn about the very basics of how late binding in C# can do the job, by creating the most primitive skeleton of a plugin and its hosting application.

The Plan

Design Overview

Let's get clear about our goal: we want to write a C# application that invokes operations or uses attributes of components it does not yet know about at the time when the application is written. To be more precise, our app of course has to know what it expects from the plugin component, otherwise it would not know what to do with it in the first place. We will express this expectation in the form of a C# interface.

We must also be more precise in what we mean when we talk about the plugin being a component, which is a term taken from UML or COM. The .NET platform calls physical artifacts like executable applications and reusable libraries assemblies. Our plugin will be such an assembly and contain at least one C# class that implements the interface that our application relies on.

Finally, this plugin interface must be available to both, the application as well as the plugin. To enable creating and building plugins without depending on the application itself, we will put the interface also into a separate assembly. The figure shows the static structure of our setup (for the sake of generality showing a second plugin), eventually leading to three assemblies:

  • The application assembly in the form of an *.exe file
  • The interface assembly in the form of a *.dll file
  • The actual plugin assembly, also in the form of a *.dll file

To Know or Not to Know

What does it mean from a technical point of view that our application will not know the concrete plugin at design time, but is able to invoke its features at runtime? The keyword here is what is referred to as late binding. Instead of statically compiling the program with the known class type of the plugin implementation, we dynamically load the plugin assembly and instantiate the plugin class.

.NET provides numerous variations to perform both of these tasks. We will use one of the simplest and most straightforward forms, where we specify the plugin assembly through its file path and the class to be instantiated from it through its class name. This can be done through a single call to:

C#
System.Activator.CreateInstanceFrom( string assemblyFilepath, string typeName );

The object returned from this method is actually only a handle to the instantiated type that still needs to be unwrapped to reveal the actual object, but we will see this further below. Let's start with the hands-on job in Visual Studio 2005 or its Visual C# Express variant.

Projects Setup

Project Browser

We create a new solution called PluginTest with a new project of the same name. To keep things simple, choose project type Console Application. Then we create the interface project that both the application and all plugins will share. As part of the existing solution, create a new project, this time of type Class Library and call it PluginInterface. The last project, also to be created within the existing solution is for the plugin itself. Again, create a Class Library, and assign it a fake plugin name, e.g. PluginX.

Since the interface project will always be shared between application and plugins, we can add this dependency manually. It is worth noting in the given context that this is a static reference, well known at design time. Therefore, we can already add the PluginInterface assembly to the references section of the two other projects. Of course, this is not possible for the dependency between application and plugin...

Next we add the types. Add an interface to the PluginInterface project (or utilize the existing default class that was created by Visual Studio) and call it IPluginInfo. Rename the generated class in project PluginX to PluginInfo. We will end up with a project structure as shown in the figure, including the two displayed references to the PluginInterface assembly.

Making It Work

Now that the projects have been set up, we will fill the classes and interface with life. The simplest functionality a plugin could possibly provide to its host is to reveal its name. Of course, much more complex tasks can be outsourced through a plugin mechanism, but the name query is enough to show the fundamental mechanism.

Therefore, we define the interface type in the PluginInterface assembly like this:

C#
namespace PluginInterface
{
    public interface IPluginInfo
    {
        string name { get; }
    }
}

The implementing plugin in the PluginX project is equally simple, just returning a fixed string:

C#
using PluginInterface;

namespace PluginX
{
    public class PluginInfo : IPluginInfo
    {
        public string name
        {
            get
            {
                return "Plugin X";
            }
        }
    }
}

The last piece is now to late-bind the plugin assembly at runtime into the application and to instantiate the corresponding type. As mentioned before, we use the simple approach of matching names. Especially for the plugin's class name, this may look like a cheat. However, it is certainly possible to require your plugin suppliers to follow a certain naming convention for the plugin's namespace and the contained class. If this is not an option, .NET provides quite a number of advanced ways to search a loaded assembly for a type. E.g. it would be possible to look for a class that actually implements the known IPluginInfo interface. But let's go back to our simple approach, which only requires two lines of code. First the assembly is loaded and the plugin class is instantiated by name. The ObjectHandle returned by this call is then unwrapped in the second step, which finally reveals the desired reference to the plugin class. This reference is casted to our IPluginInfo interface, which allows us to subsequently call all the nifty operations defined by it, including the demonstrated name query.

C#
using System.Reflection;
using System.Runtime.Remoting;
using PluginInterface;

namespace PluginTest
{
    class Program
    {
        static void Main( string[] args )
        {
            ObjectHandle oHandle = Activator.CreateInstanceFrom
		( "..\\..\\..\\PluginX\\bin\\Release\\PluginX.dll",
                "PluginX.PluginInfo" );

            IPluginInfo pluginInfo = oHandle.Unwrap() as IPluginInfo;
            Console.WriteLine( "Loaded plugin's name is: " + pluginInfo.name );
        }
    }
}

History

  • 4th June, 2006: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) BMW AG
Germany Germany
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralFile Access Permission Problem Pin
WCCheung23-Jul-10 11:41
WCCheung23-Jul-10 11:41 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.