动态加载与插件系统的初步实现(三):WinForm示例
来源:互联网 发布:非洲有网络吗 编辑:程序博客网 时间:2024/05/29 04:54
代码文件在此Download,本文章围绕前文所述默认AppDomain、插件容器AppDomain两个域及IPlugin、PluginProvider、PluginProxy3个类的使用与变化进行。
添加WinForm项目Host、类库Plugin、引用System.Windows.Forms;的类库Plugin_A与Plugin_B,其中Plugin_A、Plugin_B的项目属性中,“生成”选项卡中“输出路径”设置为..\Host\bin\Debug\,即指向Host项目的Bin目录。
考虑到WinForm项目常常涉及多级菜单构建,这里以两级菜单示例。
Plugin项目中IPlugin代码:
public interface IPlugin{ IList<String> GetMenus(); IList<String> GetMenus(String menu); void Notify(Object userState);}
其中无参方法GetMenus()提取一级菜单,有参重载GetMenus(String menu)提取二级菜单,Notify(Object userState)是两个应用程序域的通知调用。
PluginProxy继承MarshalByRefObject,代码长点:
public class PluginProxy : MarshalByRefObject, IDisposable{ private readonly static PluginProxy instance = new PluginProxy(); public static PluginProxy Instance { get { return instance; } } private PluginProxy() { } private AppDomain hostDomain = null; private PluginProvider proxy = null; public PluginProvider Proxy { get { if (hostDomain == null) { hostDomain = AppDomain.CreateDomain("PluginHost"); } if (proxy == null) { Type proxyType = typeof(PluginProvider); proxy = (PluginProvider)hostDomain.CreateInstanceAndUnwrap(proxyType.Assembly.FullName, proxyType.FullName); } return proxy; } } public void Unload() { if (hostDomain != null) { proxy = null; AppDomain.Unload(hostDomain); hostDomain = null; } } public void Dispose() { Unload(); }}
PluginProvider除构造函数外,Notify(IPlugin plugin, Object userState)方法将调用IPlugin插件的Notify方法:
public class PluginProvider : MarshalByRefObject{ [ImportMany] public IEnumerable<Lazy<IPlugin>> Plugins { get; set; } public PluginProvider() { AggregateCatalog catalog = new AggregateCatalog(); catalog.Catalogs.Add(new DirectoryCatalog(".")); CompositionContainer container = new CompositionContainer(catalog); container.ComposeParts(this); } public void Notify(IPlugin plugin, Object userState) { plugin.Notify(userState); }}
然后是插件Plugin_A、Plugin_B的实现。添加Plugin类(类名与命名空间随意)引用System.ComponentModel.Composition,加入[Export(typeof(IPlugin))]修饰。这里使用了一份XML显示菜单目录,将在得到通知后将一个Form弹出来:
[Export(typeof(IPlugin))]public class PluginA : MarshalByRefObject, IPlugin{ private String menus = @"<Component> <Net> <AuthenticationManager /> <Authorization /> <Cookie /> </Net> <IO> <ErrorEventArgs /> <FileSystemEventArgs /> </IO> </Component>"; public IList<String> GetMenus() { return XElement.Parse(menus).Elements().Select(x => x.Name.LocalName).ToArray(); } public IList<String> GetMenus(String menu) { return XElement.Parse(menus).Elements(menu).Elements().Select(x => x.Name.LocalName).ToArray(); } public void Notify(Object userState) { String text = (String)userState; Label label = new Label() { Text = text, AutoSize = false, Dock = DockStyle.Fill, TextAlign = System.Drawing.ContentAlignment.MiddleCenter, }; Form frm = new Form(); frm.Controls.Add(label); frm.ShowDialog(); }}
Plugin_B与Plugin_A类似,不再重复,然后是Host实现。Host使用了两个FlowLayoutPanel分别用于显示一级菜单与两级菜单。
Load按钮加载插件列表,将每个插件绑定到一个Button上:
private void button1_Click(object sender, EventArgs e){ flowLayoutPanel1.Controls.Clear(); textBox1.AppendText("PluginProvider loaded"); textBox1.AppendText(Environment.NewLine); PluginProvider proxy = PluginProxy.Instance.Proxy; IEnumerable<Lazy<IPlugin>> plugins = proxy.Plugins; foreach (var plugin in plugins) { foreach (var menu in plugin.Value.GetMenus()) { Button menuBtn = new Button(); menuBtn.Text = menu; menuBtn.Tag = plugin.Value; menuBtn.Click += menuBtn_Click; flowLayoutPanel1.Controls.Add(menuBtn); } }}private void menuBtn_Click(object sender, EventArgs e){ flowLayoutPanel2.Controls.Clear(); Button menuBtn = (Button)sender; try { IPlugin plugin = (IPlugin)menuBtn.Tag; foreach (var item in plugin.GetMenus(menuBtn.Text)) { Button itemBtn = new Button(); itemBtn.Text = item; itemBtn.Tag = plugin; itemBtn.Click += itemBtn_Click; flowLayoutPanel2.Controls.Add(itemBtn); } } catch (AppDomainUnloadedException) { textBox1.AppendText("Plugin domain have been uloaded"); textBox1.AppendText(Environment.NewLine); }}private void itemBtn_Click(object sender, EventArgs e){ try { Button menuBtn = (Button)sender; IPlugin plugin = (IPlugin)menuBtn.Tag; PluginProvider proxy = PluginProxy.Instance.Proxy; proxy.Notify(plugin, menuBtn.Text); } catch (AppDomainUnloadedException) { textBox1.AppendText("Plugin domain not loaded"); textBox1.AppendText(Environment.NewLine); }}
Unload按钮卸载插件AppDomain:
private void button2_Click(object sender, EventArgs e){ PluginProxy.Instance.Unload(); textBox1.AppendText("PluginProvider unloaded"); textBox1.AppendText(Environment.NewLine);}
Delete按钮移除Plugin_A.dll、Plugin_B.dll:
private void button3_Click(object sender, EventArgs e){ try { String[] pluginPaths = new[] { "Plugin_A.dll", "Plugin_B.dll" }; foreach (var item in pluginPaths) { if (System.IO.File.Exists(item)) { System.IO.File.Delete(item); textBox1.AppendText(item + " deleted"); } else { textBox1.AppendText(item + " not exist"); } textBox1.AppendText(Environment.NewLine); } } catch (Exception ex) { textBox1.AppendText(ex.Message); textBox1.AppendText(Environment.NewLine); }}
运行结果如下:
我尝试比较IEnumerable<Lazy<IPlugin>>与IEnumerable<IPlugin>的进程内存占用,在一个额外的Button里进行100加载与卸载,统计内存变化图如下,有兴趣的可以下载EXCEL文件看看:
<script type="text/javascript"><!--google_ad_client = "ca-pub-1944176156128447";/* cnblogs 首页横幅 */google_ad_slot = "5419468456";google_ad_width = 728;google_ad_height = 90;//--></script><script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"></script>
- 动态加载与插件系统的初步实现(三):WinForm示例
- 动态加载与插件系统的初步实现(二):AppDomain卸载与代理
- 动态加载与插件系统的初步实现(四):解析JSON、扩展Fiddler
- WinForm 从XML中动态加载菜单的示例
- WinForm 从XML中动态加载菜单的示例
- Java 静态加载类与动态加载类(初步认识Java的反射)
- C# 动态加载插件的实现
- Winform自动升级系统的设计与实现(源码)
- C#实现WinForm程序的动态系统托图标
- Unity 动态加载与内存(三)
- 动态加载与插件化
- 动态加载与插件化
- 加载插件(三)
- spring+webwork的动态加载实现osgi的插件思想
- Gstreamer动态插件的实现与原理
- STM32系统中应用的动态加载可行性研究过程<三>
- 【Android高级】DexClassloader和PathClassloader动态加载插件的实现
- BindingSource实现DataGridview与TextBox动态显示(绑定WinForm)
- 并发场景下HashMap.get导致cpu耗光
- Android中如何修改系统时间(应用程序获得系统权限)
- NYOJ92-图像有用区域
- uva 1364 - Knights of the Round Table(点双连通分量,5级)
- seekg()/seekp()与tellg()/tellp()的用法详解
- 动态加载与插件系统的初步实现(三):WinForm示例
- java类的初始化时机
- hdu 3299
- 阿姆达尔定律和数据库并行
- hibernate对多表的查询
- EOJ 2112 WYI
- 基本的IO操作
- 谈一谈SQL Server中的执行计划缓存(上)
- NEU1007: English Game 字典树排序 字符串处理