C# 动态加载组件类库,支持热插拔组件

新建项目IPlugin类库,里面新增IPlugin.cs(插件接口),PluginManager.cs(插件管理),FileListenerServer.cs(文件夹监控),依次如下

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace IPlugin
{
    public interface IPlugin:IDisposable
    {
        Guid Guid { get; }
        string Menu { get; }
        string Name { get; }
        void Execute();
        void Load();
        string ShowName();
    }
}
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;


namespace IPlugin
{
    public class PluginManager
    {
        /// <summary>
        /// 当前拥有的插件
        /// </summary>
        public Dictionary<string, IPlugin> Plugins { get => _plugins; }

        private Dictionary<string, IPlugin> _plugins;

        /// <summary>
        /// 文件监听
        /// </summary>
        private FileListenerServer _fileListener = null;
        private void Main(string[] args)
        {
            Console.WriteLine("可插拔插件服务");
            var dic = Directory.GetCurrentDirectory();
            var path = Path.Combine(dic, "plugIn");
            Init(path);
            // 监听文件下插件变化,实现热插拔
            _fileListener = new FileListenerServer(path, ref _plugins);
            _fileListener.Start();
            Console.WriteLine("按q/Q退出");
            while (true)
            {
                string input = Console.ReadLine();
                switch (input)
                {
                    case "q":
                        _fileListener.Stop();
                        return;
                    case "Q":
                        _fileListener.Stop();
                        return;
                    default:
                        Console.WriteLine("按q/Q退出");
                        break;
                }
            }
        }
        /// <summary>
        /// 初始化插件
        /// </summary>
        private void Init(string path)
        {
            Console.WriteLine(string.Format("==========【{0}】==========", "开始加载插件"));
            // 1.获取文件夹下所有dll文件
            DirectoryInfo directoryInfo = new DirectoryInfo(path);
            var dlls = directoryInfo.GetFiles();
            // 2.启动每个dll文件
            for (int i = 0; i < dlls.Length; i++)
            {
                // 2.1 获取程序集
                var fileData = File.ReadAllBytes(dlls[i].FullName);
                Assembly asm = Assembly.Load(fileData);
                var manifestModuleName = asm.ManifestModule.ScopeName;
                // 2.2 dll名称
                var classLibrayName = manifestModuleName.Remove(manifestModuleName.LastIndexOf("."), manifestModuleName.Length - manifestModuleName.LastIndexOf("."));
                Type type = asm.GetType("Plugin_Test" + "." + classLibrayName);
                if (!typeof(IPlugin).IsAssignableFrom(type))
                {
                    Console.WriteLine("未继承插件接口");
                    continue;
                }
                //dll实例化
                var instance = Activator.CreateInstance(type) as IPlugin;
                instance.Execute();
                _plugins.Add(classLibrayName, instance);
                //释放插件资源
                instance.Dispose();
                instance = null;
            }

            Console.WriteLine(string.Format("==========【{0}】==========", "插件加载完成"));
            Console.WriteLine(string.Format("==========【{0}】==========", "共加载插件{0}个"), _plugins.Count);
        }
    }
}
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System;

namespace IPlugin
{
    public class FileListenerServer
    {
        /// <summary>
        /// 文件监听
        /// </summary>
        private FileSystemWatcher _watcher;
        /// <summary>
        /// 插件
        /// </summary>
        private Dictionary<string, IPlugin> _iPlugin;
        /// <summary>
        /// 插件信息
        /// </summary>
        public FileListenerServer(string path, ref Dictionary<string, IPlugin> keyValuePairs)
        {

            try
            {
                _iPlugin = keyValuePairs;
                this._watcher = new FileSystemWatcher();
                _watcher.Path = path;
                _watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.DirectoryName;
                //_watcher.IncludeSubdirectories = true;
                _watcher.Created += new FileSystemEventHandler(FileWatcher_Created);
                _watcher.Changed += new FileSystemEventHandler(FileWatcher_Changed);
                _watcher.Deleted += new FileSystemEventHandler(FileWatcher_Deleted);
                _watcher.Renamed += new RenamedEventHandler(FileWatcher_Renamed);
            }

            catch (Exception ex)
            {
                Console.WriteLine("Error:" + ex.Message);
            }
        }


        public void Start()
        {
            // 开始监听
            this._watcher.EnableRaisingEvents = true;
            Console.WriteLine(string.Format("==========【{0}】==========", "文件监控已经启动..."));

        }

        public void Stop()
        {

            this._watcher.EnableRaisingEvents = false;
            this._watcher.Dispose();
            this._watcher = null;
            Console.WriteLine(string.Format("==========【{0}】==========", "文件监控已经关闭"));
        }
        /// <summary>
        /// 添加插件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void FileWatcher_Created(object sender, FileSystemEventArgs e)
        {
            Console.WriteLine(string.Format("==========【{0}】==========", "添加" + e.Name));
            var dll = new FileInfo(e.FullPath);
            var fileData = File.ReadAllBytes(dll.FullName);
            Assembly asm = Assembly.Load(fileData);
            var manifestModuleName = asm.ManifestModule.ScopeName;
            var classLibrayName = manifestModuleName.Remove(manifestModuleName.LastIndexOf("."), manifestModuleName.Length - manifestModuleName.LastIndexOf("."));
            Type type = asm.GetType("Plugin_Test" + "." + classLibrayName);
            // 这里默认不替换之前的插件内容
            if (_iPlugin.ContainsKey(classLibrayName))
            {
                Console.WriteLine("已经加载该插件");
                return;
            }
            if (!typeof(IPlugin).IsAssignableFrom(type))
            {
                Console.WriteLine($"{asm.ManifestModule.Name}未继承约定接口");
                return;
            }
            //dll实例化
            var instance = Activator.CreateInstance(type) as IPlugin;
            instance.Execute();
            _iPlugin.Add(classLibrayName, instance);
            //释放插件资源
            instance.Dispose();
            instance = null;
            Console.WriteLine(string.Format("==========【{0}】==========", "共加载插件{0}个"), _iPlugin.Count);
        }
        /// <summary>
        /// 修改插件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void FileWatcher_Changed(object sender, FileSystemEventArgs e)
        {
            string pluginName = e.Name.Split('.')[0];
            var dll = new FileInfo(e.FullPath);
            // 替换插件
            Console.WriteLine(string.Format("==========【{0}】==========", "修改" + e.Name));
            // 更新
            var fileData = File.ReadAllBytes(e.FullPath);
            Assembly asm = Assembly.Load(fileData);
            var manifestModuleName = asm.ManifestModule.ScopeName;
            var classLibrayName = manifestModuleName.Remove(manifestModuleName.LastIndexOf("."), manifestModuleName.Length - manifestModuleName.LastIndexOf("."));
            Type type = asm.GetType("Plugin_Test" + "." + classLibrayName);
            if (!typeof(IPlugin).IsAssignableFrom(type))
            {
                Console.WriteLine($"{asm.ManifestModule.Name}未继承约定接口");
                return;
            }
            var instance = Activator.CreateInstance(type) as IPlugin;
            instance.Execute();
            _iPlugin[classLibrayName] = instance;
            instance.Dispose();
            instance = null;
            // 避免多次触发
            this._watcher.EnableRaisingEvents = false;
            this._watcher.EnableRaisingEvents = true;
            Console.WriteLine(string.Format("==========【{0}】==========", "共加载插件{0}个"), _iPlugin.Count);
        }
        /// <summary>
        /// 删除插件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void FileWatcher_Deleted(object sender, FileSystemEventArgs e)
        {
            Console.WriteLine(string.Format("==========【{0}】==========", "删除" + e.Name));
            string pluginName = e.Name.Split('.')[0];
            if (_iPlugin.ContainsKey(pluginName))
            {
                _iPlugin.Remove(pluginName);
                Console.WriteLine($"插件{e.Name}被移除");
            }
            Console.WriteLine(string.Format("==========【{0}】==========", "共加载插件{0}个"), _iPlugin.Count);
        }
        /// <summary>
        /// 重命名
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void FileWatcher_Renamed(object sender, RenamedEventArgs e)
        {
            //TODO:暂时不做处理
            Console.WriteLine("重命名" + e.OldName + "->" + e.Name);
            //Console.WriteLine("重命名: OldPath:{0} NewPath:{1} OldFileName{2} NewFileName:{3}", e.OldFullPath, e.FullPath, e.OldName, e.Name);
        }
    }
}

新建插件实现类库:PluginOne,继承IPlugin接口

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace PluginOne
{
    [Serializable]
    public class PluginEntry:MarshalByRefObject,IPlugin.IPlugin
    {
        public Guid Guid => new Guid("21EC2020-3AEA-1069-A2DD-08002B30309D");

        public string Menu => "用户系统";

        public string Name => "测试权限";

        public void Dispose()
        {
            throw new NotImplementedException();
        }

        public void Execute()
        {
            throw new NotImplementedException();
        }

        public void Load()
        {
            throw new NotImplementedException();
        }

        public string ShowName()
        {
            return "PluginOne";
        }
    }
}

新增启动项目main,新建winform窗体,添加两个按钮;新建代理类ProxyObject.cs。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Windows.Forms;

using IPlugin;

namespace PluginMain
{
    public partial class Form1 : Form
    {
        string assemblyName = Assembly.GetExecutingAssembly().GetName().FullName;

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            AppDomainSetup setup = new AppDomainSetup();
            setup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
            setup.PrivateBinPath = setup.ApplicationBase;
            setup.ApplicationName = "DllTest";
            setup.ShadowCopyDirectories = setup.ApplicationBase + "@dlls";
            setup.ShadowCopyFiles = "true";
            AppDomain ad = AppDomain.CreateDomain(assemblyName, null, setup);
            var proxy = (ProxyObject)ad.CreateInstanceFromAndUnwrap("PluginMain.exe", typeof(ProxyObject).FullName);
            //var obj =Activator.CreateInstanceFrom(ad.BaseDirectory + "\\Plugin\\PluginOne.dll", "PluginOne.PluginEntry");
            IPlugin.IPlugin Plugn = proxy.Create(ad.BaseDirectory + "\\Plugin\\PluginOne.dll", "PluginOne.PluginEntry", null);
            MessageBox.Show(Plugn.ShowName());
            AppDomain.Unload(ad);
        }

        private void button2_Click(object sender, EventArgs e)
        {
            AppDomainSetup setup = new AppDomainSetup();
            setup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
            setup.PrivateBinPath = setup.ApplicationBase;
            setup.ApplicationName = "DllTest";
            setup.ShadowCopyDirectories = setup.ApplicationBase + "@dlls";
            setup.ShadowCopyFiles = "true";
            AppDomain ad = AppDomain.CreateDomain(assemblyName, null, setup);
            var proxy = (ProxyObject)ad.CreateInstanceFromAndUnwrap("PluginMain.exe", typeof(ProxyObject).FullName);
            //var obj =Activator.CreateInstanceFrom(ad.BaseDirectory + "\\Plugin\\PluginOne.dll", "PluginOne.PluginEntry");
            IPlugin.IPlugin Plugn = proxy.Create(ad.BaseDirectory + "\\Plugin\\PluginTow.dll", "PluginTow.PluginEntry", null);
            MessageBox.Show(Plugn.ShowName());
            AppDomain.Unload(ad);
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;

namespace PluginMain
{
    public class ProxyObject :MarshalByRefObject
    {
        private const BindingFlags bfi = BindingFlags.Instance | BindingFlags.Public | BindingFlags.CreateInstance;


        public ProxyObject() { }

        public IPlugin.IPlugin Create(string assemblyFile, string typeName, object[] args)
        {
            return (IPlugin.IPlugin)Activator.CreateInstanceFrom(assemblyFile, typeName, false, bfi, null, args, null, null).Unwrap();
        }
        
    }
}

 

posted @ 2024-03-24 00:48  木狼  阅读(55)  评论(0编辑  收藏  举报