zoukankan      html  css  js  c++  java
  • 我的插件架构

    上一篇博文实现了我的程序的自动升级,这篇来介绍我的程序的扩展部分--插件结构

    园子里有很多关于插件架构的文章,有大的整个框架也有小的功能代码,每每阅读都有不少收获!

    现在就来实现我的插件架构,部分内容参考学习自其它地方,在此表示感谢。

    我要实现的是给Winform程序添加扩展接口,详细的就不多说了,直接按照我自己的理解来贴代码。

    一,宿主程序提供公开接口供插件程序调用:

         插件要如何与宿主程序通讯或使用宿主程序资源呢???

         首先插件程序不可能添加对宿主程序的引用,所以首先实现一个IApplication接口,宿主程序继承自IApplication接口

         IApplication接口描述宿主程序要提供给插件程序的资源(这里所说的资源应为宿主程序愿意公开的一些东西)

    IApplication
    public interface IApplication
        {
            
    /// <summary>
            
    /// 插件工具栏
            
    /// </summary>
            ToolStrip PluginTools{get;set;}
            
    /// <summary>
            
    /// 在宿主程序添加插件工具栏
            
    /// </summary>
            void CreatePluginTools();
            
    /// <summary>
            
    /// 插入插件工具
            
    /// </summary>
            
    /// <param name="plugin"></param>
            void InsertPluginTool(IPlugin plugin);
        }

         上面的代码描述了宿主程序开放的资源,(这里的资源按照自己程序的设计需求进行提供)

         第一步解决了宿主程序要如何开放接口或资源的问题,下一步就是插件程序如何使用宿主的开放资源

    二,插件程序与宿主程序通讯或使用宿主程序公开的资源

         在第一步已经通过 IApplication 接口文件描述了宿主程序要公开的资源和接口

         插件程序要能使用这些资源和接口,则要对IApplication 接口进行实例化;

         明确了这一点,那么插件接口文件就可设计就如下:

    IPlugin
    public interface IPlugin
        {
            
    /// <summary>
            
    /// 作者
            
    /// </summary>
            string Author{get;set;}
            
    /// <summary>
            
    /// 版本
            
    /// </summary>
            string Version{get;set;}
            
    /// <summary>
            
    /// 描述
            
    /// </summary>
            string Description{get;set;}
            
    /// <summary>
            
    /// 图标
            
    /// </summary>
            Icon Ico{get;}  
            
    /// <summary>
            
    /// 是否自动加载
            
    /// </summary>
            bool AutoLoad { get;}
            
    /// <summary>
            
    /// 宿主对象
            
    /// </summary>
            IApplication application{get;set;}
            
    /// <summary>
            
    /// 加载
            
    /// </summary>
            void Load();
            
    /// <summary>
            
    /// 卸载
            
    /// </summary>
            void UnLoad();
        }

        IPlugin接口描述了插件程序的一些信息,描述了插件程序需要实现的2个接口 Load和UnLoad,以及最关键的 IApplication对象 

        有了一二两步之后,宿主程序跟插件程序如何设计都已经比较清楚了,

        无非就是宿主程序实现 IApplication 接口,插件程序实现IPlugin 接口,

        

       现在我们先来看一个实现了IPlugin 接口的插件程序

    PluginDLL
    public class PluginK : IPlugin
        {
            
    const bool autorun = false;
            
    public IApplication application { getset; }
            
    public string Author { get;set; }
            
    public string Version { getset; }
            
    public string Description { get;set; }
            
    public Icon Ico { get { return Properties.Resource.ico; } }
            
    public bool AutoLoad { get { return autorun; } }
            
    public void Load()
            {
                Console.WriteLine(
    "我是一个插件,我现在加载啦");
            }
            
    public void UnLoad()
            {
                Console.WriteLine(
    "我现在卸载啦");
            }
        }

        通过这个插件的实现,不难理解这个PluginDLL的application属性恰恰描述的就是一个宿主程序对象

       

        现在假设 IApplication接口描述了一个 CreateMoney() 的方法,宿主程序实现CreateMoney()方法,但是宿主程序每隔100年才调用一次CreateMoney()方法

        这时候我们肯定不愿意了,我们想一分钟就进行一次CreateMoney()方法,怎么办呢?

        那我们就开发一个插件 ,插件实现一个方法

        SpeedCreateMoney()

       {

           appliction. CreateMoney();

       } 

       OK,这下好了,我们的插件实现了 SpeedCreateMoney()方法来快速的造钱,而这个造钱不是插件自己造,插件自己不会造,只能通过宿主程序来造,      即appliction. CreateMoney();

     (上面的比喻不是很准确,只大概说明了一个宿主提供公开接口的一个例子)

    到这里 问题就出现了,插件实现了SpeedCreateMoney() 方法,但是插件的appliction对象却没有实现,怎么办,当然不能自己造了,只能让宿主程序来提供

    既然宿主程序提供,那宿主程序不可能依赖你插件,而只有你插件依赖宿主程序,这时候就是下一个问题咯!

    三,宿主程序加载插件

         这里用一个类来专门加载插件,或者也可以对插件进行管理,代码如下:

    PluginService
     public class PluginService:IPluginService
        {
            IApplication application;
            
    bool bCreatePluginTools = false;
            
    public PluginService(IApplication app)
            {
                application
    =app;
                plugins 
    = new Dictionary<string, IPlugin>();
            }
            
    /// <summary>
            
    /// 插件字典
            
    /// </summary>
            public Dictionary<String, IPlugin> plugins;
            
    /// <summary>
            
    /// 加载所有插件
            
    /// </summary>
            public void LoadAllPlugins()
            {
                
    string dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugin");
                
    if (!Directory.Exists(dir))
                    
    return;
                DirectoryInfo dirInfo 
    = new DirectoryInfo(dir);
                FileInfo[] dlls 
    = dirInfo.GetFiles("*.dll");
                
    foreach (FileInfo file in dlls)
                {
                    Assembly assembly 
    = Assembly.LoadFile(file.FullName);
                    
    try
                    {
                        Type[] types 
    = assembly.GetTypes();
                        
    foreach (Type type in types)
                        {
                            
    if (type.GetInterface("IPlugin"!= null)
                            {
                                IPlugin instance 
    = (IPlugin)Activator.CreateInstance(type);
                                instance.application 
    = application;
                                
    //根据XML文件读取插件属性并 set属性
                                ConvertXmlToPlugin(file, instance);
                                plugins.Add(file.Name, instance);
                                
    if (instance.AutoLoad)
                                {
                                    instance.Load();
                                }
                                
    else
                                {
                                    
    if (!bCreatePluginTools)
                                    {
                                        application.CreatePluginTools();
                                        bCreatePluginTools 
    = true;
                                    }
                                    application.InsertPluginTool(instance);
                                }
                            }
                        }

                    }
                    
    catch
                    {
                        
    //...插件加载异常  保存日志?
                    }
                }
            }
            
    /// <summary>
            
    /// 读取插件配置XML文档信息
            
    /// </summary>
            
    /// <param name="xmlInfo"></param>
            
    /// <param name="plugin"></param>
            private void ConvertXmlToPlugin(FileInfo xmlInfo,IPlugin plugin)
            {
                
    if (File.Exists(xmlInfo.FullName + ".xml"))
                {
                    XmlDocument document 
    = new XmlDocument();
                    document.Load(xmlInfo.FullName
    +".xml");
                    XmlElement ent 
    = document.DocumentElement;
                    
    foreach (XmlNode node in ent.ChildNodes)
                    {
                        
    string xmlName=node.Name;
                        
    string xmlText = node.InnerText;
                        
    switch (xmlName)
                        {
                            
    case "Author":
                                plugin.Author 
    = xmlText;
                                
    break;
                            
    case "Version":
                                plugin.Version 
    = xmlText;
                                
    break;
                            
    case "Description":
                                plugin.Description 
    = xmlText;
                                
    break;
                        } 
                    }
                    
                }
                
    else
                {
                    plugin.Author 
    = xmlInfo.Name;
                    plugin.Version 
    = xmlInfo.Name;
                    plugin.Description 
    = "缺少插件配置文件";
                }
            }
        }

       虽然这个管理类实现了一个IPluginService接口,但这并不是必须的,这里就不多说了,我们只关心宿主程序如何加载插件;

        Assembly assembly = Assembly.LoadFile(file.FullName);

                    try

                    {

                        Type[] types = assembly.GetTypes();

                        foreach (Type type in types)

                        {

                            if (type.GetInterface("IPlugin") != null)

                            {

                                IPlugin instance = (IPlugin)Activator.CreateInstance(type);

                                instance.application = application;

                                //根据XML文件读取插件属性并 set属性

                                ConvertXmlToPlugin(file, instance);

                                plugins.Add(file.Name, instance);

                                if (instance.AutoLoad)

                                {

                                    instance.Load();

                                }

                                else

                                {

                                    if (!bCreatePluginTools)

                                    {

                                        application.CreatePluginTools();

                                        bCreatePluginTools = true;

                                    }

                                    application.InsertPluginTool(instance);

                                }

                            }

                        }


                    }

                    catch

                    {

                        //...插件加载异常  保存日志?

                    } 

    代码很清晰,我们知道原来是通过 Assembly  加载程序集,在通过Activator.CreateInstance 来返回插件程序对象

    这里只返回我们需要的,也就是实现了 IPlugin接口的对象,

     OK,万事具备,有了插件程序对象,那么宿主程序就可以使用插件的方法和资源咯~~~

     一般来说,插件程序只提供Load 和 UnLoad 2个方法,宿主程序加载插件后调用 Load方法,

    而实际插件程序的Load方法中是宿主程序给插件程序的对象application 的一些公开方法和资源的使用!

    说起来是不是很绕呢,好像陷入死循环一样的, 其实这就相当于 网络编程的Server-Client  互相可以作为接收端和发送端 (比喻不欠当请谅解)

    到此为止,一个小的简单的插件结构算OK了, 在我的程序中主要做了以下工作:

    1,用XML文件描述插件程序的一些信息

    2,用AutoLoad字段标示插件是否需要在宿主加载时调用Load方法

    3,对于不在加载时就Load方法的插件,给宿主程序添加一个工具栏名为PluginTools,并将插件程序表示在Tools工具栏里,可通过Click事件进行调用插件的Load方法

    4,用一个窗体来查看宿主程序的所有插件信息 

    到此为止,上图一张,以及完整项目和使用示例

     

    完整项目下载:/Files/cxwx/Plugin.rar     宿主程序的实现示例在IApplication中有包含,插件程序的实现示例看文章中PluginDLL

    本着交流学习的目的,如有不对的地方希望大家批评指正! 

    补充说明一下:为了自己程序的需要 ,本着简化简单的原则,有些地方时按照自己需求设计的,如自动执行Load方法的插件不添加到插件工具栏等,还望大家各取所需! 

       没有太复杂的东西,只为自用与学习! 

  • 相关阅读:
    9天C#转Java学习过程,自己记录一下
    【分享】我们用了不到200行代码实现的文件日志系统,极佳的IO性能和高并发支持,附压力测试数据
    微信公众号第三方平台开发坑
    分享我们团队最近开发的微信公众号运营助手,可以在手机上回复粉丝留言
    分享一波会眨眼的壁纸
    idea连接服务器上传jar并运行
    ngnix简单使用
    IntelliJ IDEA简介及简单操作
    eclipse开发创建web项目
    myeclipse/eclipse 配置SSM框架错误之一解决方法
  • 原文地址:https://www.cnblogs.com/cxwx/p/1768395.html
Copyright © 2011-2022 走看看