winform下的dll加载与卸载 插件式应用

 

       最近在更改一个项目的,开会研究之后希望可以做成插件的方式来实现。项目本身程序是基于2.0开发的。自己做了一个打包工具,具体实现用另外一个文章来描述吧。
       根据网上搜的资料http://www.cnblogs.com/chinhr/archive/2007/09/01/878242.html 发现加载还是个很容易的事情,麻烦的是在卸载这一环节。
 
一:通过Assembly加载
      ①加载:通过Assembly加载可以直接加载,和先加载到内存中,在通过内存读取dll,我使用的是后者(因为直接加载会出现一系列问题),我使用的是Form窗体;
     
 FileStream fs = new FileStream(loadPath, FileMode.Open, FileAccess.Read);
                BinaryReader br = new BinaryReader(fs);
                byte[] bFile = br.ReadBytes((int)fs.Length);
                br.Close();
                fs.Close();
                Assembly dll = Assembly.Load(bFile);
                Type type = dll.GetType("PluginDemo.PluginDemo");
                Form form = (Form)Activator.CreateInstance(type);
                form.ShowDialog();

 

     loadPath为dll路径,dll.GetType("类库.文件名")。这样的效果就是,通过加载dll,反射来打开我的插件窗体
      ②卸载
         此方法你是通过将dll转成字节,放到内存中,再去内存中读取要反射dll,所以在你删除的时候可以直接删除。但是此方法有一个缺点,就是你只可以删除放到内存中读取的dll,但是这个dll引用的其他dll,你删除的时候会提示无法删除
二:通过AppDomain(程序域 )加载(借鉴http://www.cnblogs.com/chinhr/archive/2007/09/01/878242.html 这篇文章)
         因为Assembly并没直接提供unload方法,只提供了各种load方法,所用通过Assembly无法实现dll的卸载。然后由于插件的热插拔效果,所以我们必须想办法通过其他途径达到卸载的目的。
         这时候我们想通过在主程序域中创建子程序域,然后对子程序域提供创建和删除。创建和删除子程序域并不困难,AppDomain已经提供了相应的方法,难点是在两个程序域之间的通信。而在这里我们使用代理来达到这一个需求。通过子程序域来实现的类和窗体,一定要继承MarshalByRefObject,否则会提示当前类(窗体未序列化)
          加载代码:
               主窗体加载代码:
                    
PermissionSet perSet = new System.Security.PermissionSet(System.Security.Permissions.PermissionState.Unrestricted);
                AppDomainSetup objSetup = new AppDomainSetup();
                objSetup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
                _objAppDomain = AppDomain.CreateDomain("MyAppDomain", null, objSetup, perSet, null);
                RemoteLoaderFactory factory = (RemoteLoaderFactory)_objAppDomain.CreateInstance("DemoForm", "DemoForm.RemoteLoaderFactory").Unwrap();
                IRemoteInterface _object = factory.Create("PluginDemo.dll", "PluginDemo.MyRemoteInterface", null);
                if (_object == null)
                {
                    string strErrorMsg = "Error: " + "Couldn't load class.";
                    MessageBox.Show(strErrorMsg);
                }
                else
                {
                    _object.Run();
                }

     代理方法:

     

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

        public RemoteLoaderFactory() { }

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

 

 
             
反射的插件代码:
  
    public class MyRemoteInterface : MarshalByRefObject,IRemoteInterface
    {
        #region IRemoteInterface 成员
        public void Run()
        {
            PluginDemo pluginDemo = new PluginDemo();
            pluginDemo.ShowDialog();
        }
        #endregion
    }

 

卸载:
  卸载代码:   
    AppDomain .Unload(_objAppDomain);
相对于直接加载到内容,使用程序域来实现加载卸载的时候,可以直接删除任何它关联的dll.
 
下面是我自己做的一个demo,附上链接提供下载:AppDomainDemo
界面仿照上面的博客来的:添加程序集的时候,需要添加AppDomainDemo\PluginDemo\bin\Debug下的PluginDemo.dll,才可以,因为测试程序中写死了。
测试反射类库引用Test类库的信息的时候,记得将Test.dll放到主程序目录下,否侧会找不到的。
以上是自己查找资料总结的,所过哪里有错误,还望指正^_^,下一篇把自己做的工具上传上来
 
 
posted @ 2015-12-16 13:58  花落花开花满园  阅读(757)  评论(0编辑  收藏  举报