这是在课堂上讲的一个小范例。场景是:
1. 我们有一个主程序,它公开了一套API,允许其他开发人员为它设计插件
2. 因为插件不是我们设计的,所以我们需要确保这些插件不会恶意地伤害到用意。为此,我们希望将插件的运行权限降低。
解决方案就是,我们单独用一个AppDomain来运行这些插件,并且在该AppDomain级别单独设置安全策略。
1. API
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace API
{
public interface IPlugin
{
void Run();
}
}
2. Plugin
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace PluginSample
{
public class Plug:API.IPlugin
{
#region IPlugin 成员
void API.IPlugin.Run()
{
MainForm form = new MainForm();
form.ShowDialog();
}
#endregion
}
}
Plugin中有一个窗体,里面有一个按钮,执行访问注册表的操作
using System;
using System.Windows.Forms;
using Microsoft.Win32;
namespace PluginSample
{
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
RegistryKey key = Registry.CurrentUser.CreateSubKey("Plugins");
key.SetValue("Version", "1.0.0");
key.SetValue("Update", DateTime.Now.ToString());
MessageBox.Show("操作已经完成");
}
}
}
3. 宿主程序
为了实现跨应用程序域的加载,我们设计了一个AssemblyLoader类型
using System;
using System.Linq;
using System.Reflection;
namespace Host_WinForms
{
[Serializable]
class AssemblyLoader:MarshalByRefObject
//必须继承MarshalByRefObject,否则无法实现AppDomain级别的安全控制
{
public string[] GetPluginTypeNames(string path) {
Assembly ass = Assembly.LoadFile(path);
var query = from type in ass.GetTypes()
where type.GetInterface(typeof(API.IPlugin).FullName) != null
select type.FullName;
return query.ToArray();
}
public void RunPlugin(string path, string typeName) {
Assembly ass = Assembly.LoadFile(path);
API.IPlugin plugin = (API.IPlugin)ass.CreateInstance(typeName);
plugin.Run();
}
}
}
宿主程序有一个MainForm
using System;
using System.Windows.Forms;
using System.IO;
using System.Security.Permissions;
using System.Security.Policy;
using System.Security;
namespace Host_WinForms
{
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
private void Log(string message)
{
richTextBox1.AppendText(DateTime.Now.ToString().PadRight(25));
richTextBox1.AppendText(message);
richTextBox1.AppendText(Environment.NewLine);
}
private void MainForm_Load(object sender, EventArgs e)
{
Log("主程序启动");
Log("主程序域中的程序集:");
foreach (var item in AppDomain.CurrentDomain.GetAssemblies())
{
Log(item.FullName);
}
//加载所有的插件(遍历当前目录下面的dll),动态创建菜单项目
AppDomain plugindomain = AppDomain.CreateDomain("PluginDomain");
AssemblyLoader loader = (AssemblyLoader)plugindomain.CreateInstanceFromAndUnwrap("Host_WinForms.exe", typeof(AssemblyLoader).FullName);
foreach (var item in Directory.GetFiles(Environment.CurrentDirectory, "*.dll"))
{
foreach (var type in loader.GetPluginTypeNames(item))
{
ToolStripMenuItem menuitem = new ToolStripMenuItem(type);
menuitem.Tag = item;
menuitem.Click += (o1, e1) =>
{
//当点击了之后执行有关的插件
AppDomain plugindomaintemp = AppDomain.CreateDomain("PluginDomain");
PolicyLevel level = PolicyLevel.CreateAppDomainLevel();
PermissionSet ps = level.GetNamedPermissionSet("Internet");
ps.AddPermission(new FileIOPermission(
FileIOPermissionAccess.AllAccess, Environment.CurrentDirectory));
level.RootCodeGroup.PolicyStatement = new PolicyStatement(ps);
plugindomaintemp.SetAppDomainPolicy(level);
try
{
AssemblyLoader loadertemp = (AssemblyLoader)plugindomaintemp.CreateInstanceFromAndUnwrap(
"Host_WinForms.exe", typeof(AssemblyLoader).FullName);
ToolStripMenuItem temp = (ToolStripMenuItem)o1;
loadertemp.RunPlugin(temp.Tag.ToString(), temp.Text);
}
catch (Exception ex) {
MessageBox.Show(string.Format("运行插件发生如下错误:{0}", ex.Message));
}
finally
{
AppDomain.Unload(plugindomaintemp);
}
};
AllPluginMenu.DropDownItems.Add(menuitem);
}
}
AppDomain.Unload(plugindomain);
Log("主程序域中的程序集:");
foreach (var item in AppDomain.CurrentDomain.GetAssemblies())
{
Log(item.FullName);
}
}
}
}
4. 运行效果