二次开发,加载DLL,模块化,前两天问了下模块化,现在做了个例子请大家指教
窗口应用程序 命名为Main
窗口加载事件
- C# code
private void Main_Load(object sender, EventArgs e) { //当前程序集路径 string dir = Assembly.GetEntryAssembly().Location; //string dir =Application.ExecutablePath; dir = Path.GetDirectoryName(dir); if (!Directory.Exists(dir)) { MessageBox.Show("读取DLL失败!"); return; } //扫描下所有的dll文件 string[] dlls = Directory.GetFiles(dir, "*.dll"); foreach (string item in dlls) { Assembly asm = Assembly.LoadFile(item); Type[] typeArray = asm.GetExportedTypes(); foreach (Type typeitem in typeArray) { Type typeIExtension = typeof(I_Extension.I_Extension); //必须是实现了I_Extension接口,并且不能是抽象类 if (typeIExtension.IsAssignableFrom(typeitem) && !typeitem.IsAbstract) { I_Extension.I_Extension IExtension = (I_Extension.I_Extension)Activator.CreateInstance(typeitem); IExtension.Function(this); } } } }
接口类项目I_Extension
- C# code
public interface I_Extension { /// <summary> /// 插件名称 /// </summary> string Name { set; get; } /// <summary> /// 插件执行内容 /// </summary> void Function(System.Windows.Forms.Form form); }
插件DLL项目
- C# code
public class MenuStrip : I_Extension.I_Extension { private const string SqlCon = "server=.;database=GT_Menu;uid=sa;pwd=000110"; private string name = "菜单控件"; public string Name { get { return name; } set { name = value; } } public void Function(Form form) { InitializeComponent(form); Bind(); } private System.Windows.Forms.MenuStrip menuStrip1; private void InitializeComponent(Form form) { menuStrip1 = new System.Windows.Forms.MenuStrip(); // // menuStrip1 // menuStrip1.Name = "menuStrip1"; menuStrip1.TabIndex = 0; menuStrip1.Text = "menuStrip1"; // // // form.MainMenuStrip = menuStrip1; form.Controls.Add(menuStrip1); } private void Bind() { DataTable dt = SqlHelper.ExecuteDataset(SqlCon, CommandType.Text, "select * from Menu_DLL", null).Tables[0]; DataRow[] dr0 = dt.Select("[MenuItmeID] = 0"); for (int i = 0; i < dr0.Length; i++) { ToolStripMenuItem tsmi = new ToolStripMenuItem(dr0[i]["MenuItmeDLL"].ToString()); D_MenuID(dt, Convert.ToInt32(dr0[i]["MenuID"].ToString()), ref tsmi); menuStrip1.Items.Add(tsmi); } } void D_MenuID(DataTable dt, int MenuItmeNum, ref ToolStripMenuItem tsmi) { foreach (DataRow dr in dt.Select("[MenuItmeID] = " + MenuItmeNum.ToString())) { ToolStripMenuItem tsmitem = new ToolStripMenuItem(dr["MenuItmeDLL"].ToString()); tsmitem.Tag = dr["MenuID"].ToString(); tsmi.DropDownItems.Add(tsmitem); D_MenuID(dt, Convert.ToInt32(dr["MenuID"].ToString()), ref tsmitem); } } }
问题1: 这样写我成功的加载了DLL并执行了一些操作,但是这样把FORM窗体直接当参数传递是否会出现什么问题?
问题2: 这样写对资源回收,清理工作 怎么来解决,还是他自动回收了?
问题3: 希望大侠帮我改进方案,说说我写的不足或者思路是否正确,我想做个能二次开发,可扩展性强的软件,这样是否可行,需要主意那些地方?
[解决办法]
我来看看啊
3Q啊
[解决办法]
一般不要把界面元素当参数传递。
因为变化可能性最多的就是界面了。你今天是Form,明天变成Control,后天又变成网页怎么办?
注意数据处理和界面要分开。因为基本数据结构相对是稳定的。
然后你的反射的搞法不是这样的,你这样似乎是叫“试探性”的反射,完全没有效率和可控性。
[解决办法]
1.首先对LZ不采用MEF解决方案感到些许失望
2.但是LZ的方法并无明显不妥,当然前提是能保证插件的代码是安全的,不然别人做个插件把你的Form给Dispose掉就不好了。
3.资源回收方面,由于LZ直接将程序集加载到当前应用程序域,故直到当前应用程序域卸载之前,程序集不会被卸载。至于通过反射创建的对象,他们与一般对象比起来,并不享有更多(或更少)的特权,同样受到GC的管理。
[解决办法]
最好不用向插件里面传任何窗体或者控件,你完全可以定义自己的菜单接口,然后在主程序里面加载插件的菜单而不是把主窗体传过去。接口里面可以定义菜单的点击方法,方便插件处理自己的事务,也可以定义事件供主程序处理,比如item的enabled等处理
另外插件可能有多个,最好在主程序中保存所有插件实例,这样最多就是启动的时候读取插件稍微耽误点时间,使用的时候不会影响效率
[解决办法]
忘记了补充一句,支持插件是为了用来方便扩充功能的,但是同时也要考虑安全的问题。如果你能保证这个插件的维护者只有你自己而不是开放给其他的程序员甚至于开放给用户使用,那么你传窗体过去还没啥,否则可就危险了,主窗体都交给人家了,人家在上面干点啥不行?
[解决办法]
[解决办法]
[解决办法]
.Net 4.0正式发布两年有余……
关于资源回收部分,程序启动时会创建一个默认的应用程序域,程序使用到的程序集均加载在该应用程序域中。你的程序加载插件时也采用了同样的方法,所以直到应用程序域卸载即程序退出时,插件才能卸载。
[解决办法]
其实无论是资源释放还是安全问题都是可以解决的,但代价是调用变得不是那么方便。
MSDN:如何:运行沙盒中部分受信任的代码
采用相同的手段——把插件加载到不同的应用程序域,通过代理与其交互——接近.Net Remoting的思想,通过卸载应用程序域就能将插件卸载。这类文章网上相当多,稍微搜一下就能看到。
[解决办法]
在还没开始做
就又开始提拔资源释放了
先做了再说嘛
先写他100插件
然后段程序不停的调用
看看性能到底怎么样