最近在更改一个项目的,开会研究之后希望可以做成插件的方式来实现。项目本身程序是基于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放到主程序目录下,否侧会找不到的。
测试反射类库引用Test类库的信息的时候,记得将Test.dll放到主程序目录下,否侧会找不到的。
以上是自己查找资料总结的,所过哪里有错误,还望指正^_^,下一篇把自己做的工具上传上来