C#构建可扩展的应用程序(插件)
构建可扩展的应用程序,特别是对于WinForm应用程序是特别有好处的。我们知道,企业的需求是瞬息万变的,企业在使用软件的过程中,很可能对于现有的需求有变动甚至是提出新的需求来,可是我们的软件已经部署在企业的各个客户端中,要想将根据企业新的需求编写的模块集成到现有程序中去,我们必须重新编译整个软件,然后打包再进行重新部署,这无疑有非常大的工作量。怎样才能将新编写的模块集成到现有程序中去,而又不用重新编译整个应用程序?这就是我们接下来要讨论的话题。
利用C# 构建可扩展的应用程序,就是利用.Net的反射机制以及特性编程实现,原理我就不介绍了,很多C#的书籍都说的很清楚,接下来,我将演示如何搭建可扩展的应用程序。
一、首先建立一个类库IplugTypes
该类库定义了插件程序必须遵循的接口Iplug和一个自定义特性,在该类库中导入System.Windows.Forms的引用,代码如下:
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Windows.Forms;
6
7 namespace IplugTypes
8 {
9 /// <summary>
10 /// 插件必须继承该接口规范
11 /// </summary>
12 public interface Iplug
13 {
14 void FormShow(Form mainForm);
15 }
16 [AttributeUsage(AttributeTargets.Class)]
17 public sealed class AssemblyInfoAndCompanyInfoAttribute : System.Attribute
18 {
19 /// <summary>
20 /// 程序集名称(不包括扩展名)
21 /// </summary>
22 public string AssemblyName { getset; }
23 /// <summary>
24 /// 程序集版本
25 /// </summary>
26 public string AssemblyVersion { getset; }
27 /// <summary>
28 /// 公司名称
29 /// </summary>
30 public string CompanyName { getset; }
31 /// <summary>
32 /// 菜单名(在承载的主程序中要显示的名称)
33 /// </summary>
34 public string MenuName { getset; }
35 public AssemblyInfoAndCompanyInfoAttribute()
36 {
37
38 }
39
40 }
41 }
二、建立WinForm插件程序
建立一个WinForm窗体程序,引入上面建立的IplugTypes类库的引用,如下图:
在该窗体类中继承IPlug接口,在Iplug接口的FormShow方法中实例化本窗体,同时用AssemblyInfoAndCompanyInfo自定义属性类描述该窗体类,代码如下:
2 using System.Linq;
3 using System.Text;
4 using System.Windows.Forms;
5 using IplugTypes;
6
7 namespace WinFormIPlug
8 {
9 [AssemblyInfoAndCompanyInfo(AssemblyName = "WinFormIPlug",AssemblyVersion="1.0.0.0",CompanyName="DTXY",MenuName="C#插件")]
10 public partial class Form1 : Form,Iplug
11 {
12 public Form1()
13 {
14 InitializeComponent();
15 }
16
17 private void button1_Click(object sender, EventArgs e)
18 {
19 MessageBox.Show("这是一个插件程序!");
20 }
21
22 void Iplug.FormShow(Form mainForm)
23 {
24 Form1 fm = new Form1();
25 fm.MdiParent = mainForm;
26 fm.Show();
27 // throw new NotImplementedException();
28 }
29 }
30 }
将该窗体的输出类型设置为类库,然后进行编译,如下图
三、构建可扩展的应用程序
建立一个名称为WindowsFormMain的窗体应用程序,添加menuStrip控件,并设置该窗体的IsMdiContainer属性为True,指示该窗体为MDI对文档父窗体。如下图:
引入对程序集IplugTypes的引用,导入System.Reflection的命名空间。然后,添加FrmAdd窗体,用于导入插件程序,如下图:
同样在FrmAdd窗体类中引入对程序集IplugTypes的引用,导入System.Reflection的命名空间,该窗体类的代码如下:
2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.Data;
5 using System.Drawing;
6 using System.Linq;
7 using System.Text;
8 using System.Windows.Forms;
9 using IplugTypes;
10 using System.Reflection;
11 using System.IO;
12
13 namespace WindowsFormMain
14 {
15 public partial class FrmAdd : Form
16 {
17 public FrmAdd()
18 {
19 InitializeComponent();
20 }
21 /// <summary>
22 /// 插件加载路径
23 /// </summary>
24 public string Path { getprivate set; }
25
26 private void btnBrowse_Click(object sender, EventArgs e)
27 {
28
29 if (this.openFileDialog1.ShowDialog() == DialogResult.OK)
30 {
31 if (this.openFileDialog1.SafeFileName.Split('.')[1].ToUpper() != "DLL")
32 {
33 MessageBox.Show("您选择的不是动态类型库,扩展名为.DLL!");
34 return;
35 }
36 string strFilePath = this.openFileDialog1.FileName;
37 Path = strFilePath;
38 this.textBox1.Text = Path;
39 if (!LoadAssembly(strFilePath))
40 {
41 MessageBox.Show("插件加载失败,请确认插件是否支持Iplug接口!");
42 }
43 }
44 }
45 private bool LoadAssembly(string strFilePath)
46 {
47 bool isRight = false;
48 try
49 {
50 Assembly asm = Assembly.LoadFrom(strFilePath);
51 var types = from t in asm.GetTypes()
52 where t.IsClass && t.GetInterface("Iplug"!= null
53 select t;
54 if (types.Count() <= 0)
55 {
56