Click here to Skip to main content
Click here to Skip to main content
Articles » Languages » C# » General » Downloads
 
Add your own
alternative version

A simple plug-in engine using Reflection

, 26 Sep 2012 CPOL
This article describes how to create and use configurable plug-ins in your application.
using System;
using System.IO;
using System.Linq;
using System.Security;
using System.Reflection;
using System.Collections.Generic;
using System.Threading;

namespace PluginDemo
{
    internal sealed class PluginManager
    {
        /// <summary>
        /// Plugins directory
        /// </summary>
        private const string pluginDir = "Plugins";

        /// <summary>
        /// Dictionary, that contains instances of assemblies for loaded plugins
        /// </summary>
        public Dictionary<Assembly, PluginBase.PluginBase> Plugins { get; private set; }

        public PluginManager()
        {
            Plugins = new Dictionary<Assembly, PluginBase.PluginBase>();
            
            // There are not references to plugins assemblies in the project.
            // That's why ConfigurationErrorsException will be thrown during
            // loading of the plugin's configuration. Actually this happens
            // because an assembly defined in a configuration file couldn't be resolved.
            // We need to manually resolve that assembly.
            AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;

            string path = Path.Combine(Path.GetDirectoryName(Assembly.GetCallingAssembly().Location), pluginDir);
            if (!Directory.Exists(path))
            {
                Log.Write(String.Format("Directory {0} not found. Plugins haven't been loaded!", pluginDir), System.Diagnostics.EventLogEntryType.Warning);
            }
            else
            {
                try
                {
                    foreach (FileInfo f in new DirectoryInfo(path).GetFiles("*.dll"))
                        if (!LoadPlugin(Path.Combine(path, f.Name)))
                            Log.Write("Can't load the plugin " + f.Name, System.Diagnostics.EventLogEntryType.Warning);
                }
                catch (SecurityException)
                {
                    Log.Write("Can't get access to the directory " + pluginDir, System.Diagnostics.EventLogEntryType.Error);
                }
            }
        }

        /// <summary>
        /// Loads a plugin
        /// </summary>
        /// <param name="FullName">Full path to a plugin's file</param>
        /// <returns>"true" after successful loading, else "false"</returns>
        public bool LoadPlugin(string FullName)
        {
            Assembly pluginAssembly;
            try
            {
                pluginAssembly = Assembly.LoadFile(FullName);
            }
            // Can't load the assembly's file
            catch (Exception ex)
            {
                Log.Write(ex.Message, System.Diagnostics.EventLogEntryType.Error);
                return false;
            }
            try
            {
                Type t = pluginAssembly.GetTypes().FirstOrDefault(x => x.BaseType == typeof(PluginBase.PluginBase));

                PluginBase.PluginBase plugin = (PluginBase.PluginBase)Activator.CreateInstance(t);
                Plugins.Add(pluginAssembly, plugin);
                
                Log.Write(String.Format("Plugin has been loaded: {0}, {1}.", plugin.Name, plugin.Description));
                try
                {
                    plugin.Configuration =
                        (PluginBase.ConfigurationBase)typeof(PluginBase.ConfigurationBase)
                        .GetMethod("Open").MakeGenericMethod(pluginAssembly.GetTypes()
                        .FirstOrDefault(x => x.BaseType == typeof(PluginBase.ConfigurationBase)))
                        .Invoke(null, new object[] { Path.GetFileNameWithoutExtension(FullName), Assembly.GetCallingAssembly().Location });
                }
                // There is not a configuration section's class in the plugin or an error occured during its loading
                catch (Exception ex)
                {
                    Log.Write(String.Format("Can't load configuration for the plugin {0}: {1}", pluginAssembly.GetName().Name, ex.Message), System.Diagnostics.EventLogEntryType.Error);
                    // Working with default configuration
                }
                return true;
            }
            catch (Exception ex)
            {
                Log.Write(ex.Message, System.Diagnostics.EventLogEntryType.Error);
                return false;
            }
        }

        /// <summary>
        /// Unloads a plugin. It just removes the plugin from the dictionary and doesn't unload the assembly from the AppDomain. 
        /// </summary>
        /// <param name="Name">Plugin's assembly full name</param>
        public void UnloadPlugin(string Name)
        {
            try
            {
                Plugins.Remove(Plugins.Keys.FirstOrDefault(x => x.FullName == Name));
            }
            catch
            {
                Log.Write(String.Format("Can't unload plugin {0}", Name), System.Diagnostics.EventLogEntryType.Error);
            }
        }

        private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            return Plugins.Keys.FirstOrDefault(x => x.FullName == args.Name);
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

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

Share

About the Author

Vitaly Zhukov
Software Developer
Russian Federation Russian Federation
No Biography provided

| Advertise | Privacy | Mobile
Web03 | 2.8.141022.2 | Last Updated 27 Sep 2012
Article Copyright 2012 by Vitaly Zhukov
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid