插件架构实战
日期: 2007-3-12
Copyleft (C) 创世纪
本文通过一个简单的例子,展示基于插件的框架设计,时间仓促,错误难免,欢迎指正!
1) 加载插件需要的配置文件类 (序列化为XML文件)
using System;
namespace LoadPlugins
{
/// <summary>
/// 插件接口
/// </summary>
public class Plug
{
/// <summary>
/// 菜单名称
/// </summary>
public string[] MenuName;
/// <summary>
/// 链接库名称
/// </summary>
public string[] DllName;
/// <summary>
/// 所属菜单、次序
/// </summary>
public string[] Parent;
/// <summary>
/// 构造函数名称
/// </summary>
public string[] ConstructorName;
}
}
2) 反序列化XML文件, 获取需要加载的插件对象
using System;
using System.Windows.Forms;
using System.IO;
using sx = System.Xml;
using sr = System.Reflection;
namespace LoadPlugins
{
/// <summary>
/// 包含序列化和反序列化、反射的一组重要静态方法
/// </summary>
public class Serializer
{
/// <summary>
/// 从XML中反序化出对象 [ 抽取需要加载的插件对象 ]
/// </summary>
public static Plug Deserialize( )
{
string path = Application.StartupPath + "//configuration//COM.XML";
if( !File.Exists( path ) )
{
return null;
}
Plug a = null;
try
{
sx.Serialization.XmlSerializer xs = new System.Xml.Serialization.XmlSerializer( typeof( Plug ) );
Stream s = File.Open( path, FileMode.Open );
a = xs.Deserialize( s ) as Plug;
s.Close();
}
catch( Exception ex )
{
MessageBox.Show( ex.Message );
}
return a;
}
/// <summary>
/// 序列化到XML文档 [ 将需要加载为插件的对象序列化到XML文件 ]
/// </summary>
/// <param name="MenuName">菜单名称</param>
/// <param name="DllName">DLL名称</param>
/// <param name="Parent">所属菜单、次序</param>
public static void Serialize( string[] MenuName, string[] DllName, string[] Parent, string[] ConstructorName )
{
Plug a = new Plug( );
a.DllName = DllName;
a.MenuName = MenuName;
a.Parent = Parent;
a.ConstructorName = ConstructorName;
string path = Application.StartupPath + "//configuration//COM.XML";
try
{
Stream s = File.Create( path );
new sx.Serialization.XmlSerializer( a.GetType() ).Serialize( s, a );
s.Close();
}
catch( Exception ex )
{
MessageBox.Show( ex.Message );
}
}
/// <summary>
/// 打开插件
/// </summary>
/// <param name="dllName">插件名称</param>
/// <param name="ConstructorName">构造函数名称</param>
public static void OpenForm( string dllName, string ConstructorName )
{
string path = Application.StartupPath + @" /plug-ins/" + dllName; //路径
try
{
sr.Assembly ass = sr.Assembly.LoadFrom( path );
dllName = dllName.Replace( ".dll", string.Empty );
Type t = ass.GetType( dllName + "." + ConstructorName, false, true ); //获取类型,忽略大小写和加载异常
object o = System.Activator.CreateInstance( t ); //创建实例
Form f = o as Form; //转换得到实例
f.ShowDialog();
}
catch( Exception e )
{
MessageBox.Show( e.Message );
}
}
/// <summary>
/// 根据插件名称添加菜单
/// </summary>
/// <param name="mm">主菜单</param>
/// <param name="dNetMenu1">DNetMenu对象</param>
/// <param name="a">菜单定义对象</param>
public static void AddPlugin( Menu mm, YuanFeiSoftware.YuanFeiXPControls.DNetMenu dNetMenu1, Plug a )
{
int i = a.DllName.Length;
if( a == null )
{
return;
}
for( int m = 0; m < i; m ++ )
{
MenuItem mi = new MenuItem( );
mi.Click += new EventHandler( mi_Click );
dNetMenu1.SetMenuIcon( mi, null );
dNetMenu1.SetSideBarForeColor( mi, System.Drawing.Color.Cornsilk );
dNetMenu1.SetSideBarGradientColorOne( mi, System.Drawing.SystemColors.InactiveCaption );
dNetMenu1.SetSideBarGradientColorTwo( mi, System.Drawing.SystemColors.ActiveCaption );
dNetMenu1.SetSideBarText( mi, null );
mi.Text = a.MenuName[ m ];
mm.MenuItems[ int.Parse( a.Parent[ m ].Substring( 0, 1 ) ) ].MenuItems.Add( mi );
}
}
/// <summary>
/// 处理菜单点击事件
/// </summary>
/// <param name="sender">数据</param>
/// <param name="e">数据</param>
public static void mi_Click( object sender, System.EventArgs e )
{
Plug a = Deserialize();
if( a == null )
{
return;
}
MenuItem mi = sender as MenuItem;
int i = 0;
for( ; i < a.MenuName.Length; i ++ )
{
if( a.MenuName[ i ] == mi.Text )
{
string DllName = a.DllName[ i ];
OpenForm( DllName + ".dll", a.ConstructorName[ i ] );
break;
}
}
}
}
}
3)XML配置文件看起来应该是这个样子:(加载一个菜单名称为“开创世纪”的DLL)
<?xml version="1.0"?>
<Plug xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<MenuName>
<string>开创世纪(&C)</string>
</MenuName>
<DllName>
<string>MyFuture</string>
</DllName>
<Parent>
<string>1-5</string>
</Parent>
<ConstructorName>
<string>Future</string>
</ConstructorName>
</Plug>
4) 在主窗口的构造方法内调用相应方法,完成DLL的加载:
LoadPlugins.Serializer.AddPlugin( this.mainMenu1, this.dNetMenu1, LoadPlugins.Serializer.Deserialize() );
如上所述,一个简单的插件系统就这样设计成了,下面测试一下。将需要加载的DLL拷贝到启动目录下的plug-ins目录下,修改配置文件,然后看起来是这个样子。
效果图: