Click here to Skip to main content
12,764,163 members (37,397 online)
Click here to Skip to main content
Articles » Languages » C# » General » Downloads

Stats

39.5K views
965 downloads
97 bookmarked
Posted 17 Jun 2012

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

You may also be interested in...

Pro
Pro
Permalink | Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.170217.1 | Last Updated 27 Sep 2012
Article Copyright 2012 by Vitaly Zhukov
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid