Example plugin system

    I came up with such a task as how to develop a system of plug-ins (add-ins) for the program. Digging a little AddIn which were offered since .NET 3.5, I realized that they do not suit me. Firstly, this is not a convenient directory location, a lot of extra. Secondly, it is not possible to infiltrate into any part of the process and do it your own way, i.e. low extensibility.
    And he decided to invent a bicycle ...

    I based on isolation from the main application and the possibility of a sandbox. All this is not difficult to do by creating a new AppDomain and configuring it on InternetZone. But during development, I ran into a problem when loading the assembly with the plugin into the domain. The problem was a communications error. For loading I use AppDomain.Load (). As it turned out later, this method does not suit me. Therefore, I did not have to make a large crutch, which I will talk about.

    Let's start by creating a new project and a console application (I named it PluginHost) and add another class library project (let's call it PluginFramework). Add the class to the library:
    1. public abstract class Plugin: MarshalByRefObject
    2. {
    3.   public virtual void Initialize()
    4.   {
    5.   }
    6. }
    * This source code was highlighted with Source Code Highlighter.

    And one more class, without which it was difficult to implement the logic I needed:
    1. public class AssemblyHelper: MarshalByRefObject
    2. {
    3.   private AppDomain _currentDomain;
    4.   public AssemblyHelper()
    5.   {
    6.    _currentDomain = AppDomain.CurrentDomain;
    7.    _currentDomain.AssemblyResolve += new ResolveEventHandler(_currentDomain_AssemblyResolve);
    8.   }
    9.  
    10.   Assembly _currentDomain_AssemblyResolve(object sender, ResolveEventArgs e)
    11.   {
    12.    string[] nameSplit = e.Name.Split(',');
    13.    string path = Path.Combine(SearchFolder, nameSplit[0] + ".dll");
    14.    Assembly loadedAssembly;
    15.    try
    16.    {
    17.      loadedAssembly = Assembly.LoadFile(path);
    18.    }
    19.    catch (Exception exc)
    20.    {
    21.      Exception exp = exc;
    22.      throw;
    23.    }
    24.    if (loadedAssembly != null)
    25.    {
    26.      return loadedAssembly;
    27.    }
    28.    else
    29.    {
    30.      return null;
    31.    }
    32.   }
    33.  
    34.   public string SearchFolder { get; set; }
    35. }
    * This source code was highlighted with Source Code Highlighter.

    The base frame is ready. Not a big digression. What exactly did I want to achieve:
    1. Once again, isolation from the main application
    2. At any time, loading and unloading the plug-in. If, for example, a plugin crashes, we simply unload it and reload

    it. What’s in our Main:
    First, I load the PluginFramework assembly in reflection-only mode and get the Type of our base class for all plugins:
    1. Assembly frameworkReflect = Assembly.ReflectionOnlyLoadFrom(Path.Combine(Environment.CurrentDirectory, "PluginFramework.dll"));
    2. Type basePluginType = frameworkReflect.GetType("MyPluginSample.Framework.Plugin");
    * This source code was highlighted with Source Code Highlighter.

    Then search in all available folders
    1. string pathPlugins = Path.Combine(Environment.CurrentDirectory, "Plugins");
    2. DirectoryInfo directoryPlugins = new DirectoryInfo(pathPlugins);
    3. foreach (var dir in directoryPlugins.GetDirectories())
    4. {
    5.   FileInfo dllFile = dir.GetFiles(dir.Name + ".dll").First();
    * This source code was highlighted with Source Code Highlighter.

    And again, I load the assembly in reflection-only mode, but with the plugin and look for a class that inherits from the Plugin base class. So that we can load the assembly with the plug-in here and compare it with the base type, we had to load it at the beginning of PluginFramework only for reflection.
    1. Assembly reflectionAsm = Assembly.ReflectionOnlyLoadFrom(dllFile.FullName);
    2. Type typePlugin = reflectionAsm.GetTypes().First(t => t.IsSubclassOf(basePluginType));
    * This source code was highlighted with Source Code Highlighter.

    Now you can start creating a new domain, load everything you need into it and run our plugin to execute
    1. AppDomain pluginDomain = AppDomain.CreateDomain(dir.Name + " plugin");
    2. AssemblyHelper helper = (AssemblyHelper)pluginDomain.CreateInstanceAndUnwrap("PluginFramework", "MyPluginSample.Framework.AssemblyHelper");
    3. helper.SearchFolder = dir.FullName;
    4. Plugin plugin = (Plugin)pluginDomain.CreateInstanceAndUnwrap(reflectionAsm.FullName, typePlugin.FullName);
    5. plugin.Initialize();
    * This source code was highlighted with Source Code Highlighter.

    That's all!
    And write a test plugin to test
    1. public class MyPlugin1: Plugin
    2. {
    3.   public override void Initialize()
    4.   {
    5.    Console.WriteLine("Executing in Plugin 1. Domain Id: {0}", AppDomain.CurrentDomain.Id);
    6.   }
    7. }
    * This source code was highlighted with Source Code Highlighter.

    The directory structure is as follows Link to sources PluginSystem.zip P.S. I load the assemblies for reflection, because if we simply upload the assembly to a new domain, we will not be able to go through it from the child domain and find the class we need. The CLR will try to load this assembly into a child domain





    Also popular now: