先简单了解一下C#下插件框架。插件一般就是定义了某个特定接口的并被动态加载的动态库。应用程序启动后,可以查找(比如某个特定目录)、动态加载、识别(某个特定接口)、使用插件(调用接口函数等)。
现在.net库中有了两套插件的框架:
|
MAF: Managed Add-in Framework |
从VS2008(.NET3.5)开始 |
|
MEF: Managed Extensibility Framework |
从VS2010(.NET4.0)开始 |
这两个的优劣,网上已经有了很多的讨论。最主要的可能就是:(fixme)
- MAF使用比较繁琐,但可以将主程序和插件完全隔离
- 即使某个插件崩溃了主程序也不受影响。可以动态更新和卸载插件
- 不同版本的插件、主程序兼容性容易实现。
- MEF使用简单,主程序和插件不需要明确的区分
- 似乎大家都更看好这个,VS2010自身采用的也是MEF?
MAF
|
主程序 |
程序接口 |
接口 |
插件接口 |
插件 | ||
|
Host |
Host viewsof Add-ins |
Contracts |
Add-In views |
Add-In | ||
|
adapters |
adapters | |||||
看起来好复杂啊,但似乎还不是太难理解:
- 对于传统方式的插件,我们定义一个接口,插件提供该接口实现,程序通过接口操作
- 在这儿,首先定义一个接口(称为Contract),但是程序和插件都不用关心这个Contract具体是什么样子,它们只关心各自的接口(View),View和Contract之间通过适配器进行适配。
凭空多出这个多东西,而且应该程序要能找到它们,故MAF都它们的位置有要求:
|
类别 |
位置(某个共同目录的子目录) |
|
HostSideAdapters |
PathOfDbzhang\HostSideAdapters |
|
Contracts |
PathOfDbzhang\Contracts |
|
AddInSideAdapters |
PathOfDbzhang\AddInSideAdapters |
|
AddInViews |
PathOfDbzhang\AddInViews |
|
AddIns |
PathOfDbzhang\AddIns\XXXX |
MAF 例子
这个东西太麻烦了,恩,抛开VS集成环境,直接使用命令行来试试一个简单的例子。
|
源文件 |
==> |
最终产物 |
说明 |
|
Application.cs |
Application.exe |
我们的程序 | |
|
HostAddInView.cs |
MyHostAddInView.dll | ||
|
HostSideAdapter.cs |
HostSideAdapters/ MyHostSideAdapter.dll | ||
|
AddInContract.cs |
Contracts/ MyAddInContract.dll |
接口 | |
|
AddInSideAdapter.cs |
AddInSideAdapters/ MyAddInSideAdapter.dll | ||
|
AddInView.cs |
AddInViews/ MyAddInView.dll | ||
|
Addin1.cs |
AddIns/XXXX/Addin1.dll |
插件 |
应用程序
-
定义程序操作的接口HostAddInView.cs :
namespace CalcHVAs
{
public abstract class Calculator
{
public abstract double Calc(double a, double b);
}
}
- 定义我们的程序 Application.cs(它只操作我们前面刚定义的接口):
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.AddIn.Hosting;
using CalcHVAs;
namespace MathHost
{
class Program
{
static void Main()
{
// Assumes that the current directory is the addins root directory.
String addInRoot = Environment.CurrentDirectory;
// Search for addins.
AddInStore.Update(addInRoot);
Collection<AddInToken> tokens = AddInStore.FindAddIns(typeof(Calculator), addInRoot);
// Select an add-in.
if (tokens.Count == 0) {
Console.WriteLine("No plugins available.");
return;
}
AddInToken calcToken = ChooseCalculator(tokens);
//Activate the AddIn in a new application domain with the Internet trust level.
Calculator calc = calcToken.Activate<Calculator>(AddInSecurityLevel.Internet);
//Run the add-in.
RunCalculator(calc);
}
private static AddInToken ChooseCalculator(Collection<AddInToken> tokens)
{
Console.WriteLine("Available Plugins: ");
int tokNo = 0;
foreach (AddInToken tok in tokens) {
Console.WriteLine("\t[{0}]: {1}", tokNo.ToString(), tok.AssemblyName);
tokNo++;
}
Console.WriteLine("Which calculator do you want to use?");
String line = Console.ReadLine();
int selection;
if (Int32.TryParse(line, out selection)) {
if (selection < tokens.Count) {
return tokens[selection];
}
}
return tokens[0];
}
private static void RunCalculator(Calculator calc)
{
Console.Write("Input two numbers such as 2 3 (Or type <q> to exit). \n%> ");
String line = Console.ReadLine();
while (!line.Equals("q")) {
try {
String[] parts = line.Split(' ');
double a = Double.Parse(parts[0]);
double b = Double.Parse(parts[1]);
Console.Write("{0}\n%> ", calc.Calc(a, b));
} catch {
Console.Write("Invalid command: {0} \n%> ", line);
}
line = Console.ReadLine();
}
}
}
}
插件
-
定义插件需要实现的接口 AddInView.cs
using System;
using System.AddIn.Pipeline;
namespace CalcAddInViews
{
[AddInBase()]
public abstract class Calculator
{
public abstract double Calc(double a, double b);
}
}
- 实现该接口Addin1.cs
using System.AddIn;
using CalcAddInViews;
namespace CalcAddIns
{
[AddIn("Dbzhang800 AddIn",Version="1.0.0.0")]
public class AddInCalcV1 : Calculator
{
public override double Calc(double a, double b)
{
return a + b;
}
}
}
接口适配
-
定义接口 AddInContract.cs :
using System.AddIn.Contract;
using System.AddIn.Pipeline;
namespace CalculatorContracts
{
[AddInContract]
public interface ICalc1Contract : IContract
{
double Calc(double a, double b);
}
}
将应用程序和接口的接口和该接口分别进行适配
-
HostSideAdpater.cs
using System;
using System.AddIn.Pipeline;
using CalcHVAs;
using CalculatorContracts;
namespace CalcHostSideAdapters
{
[HostAdapterAttribute()]
public class CalculatorContractToViewHostSideAdapter : Calculator
{
private ICalc1Contract _contract;
private System.AddIn.Pipeline.ContractHandle _handle;
public CalculatorContractToViewHostSideAdapter(ICalc1Contract contract)
{
_contract = contract;
_handle = new ContractHandle(contract);
}
public override double Calc(double a, double b)
{
return _contract.Calc(a, b);
}
}
}
-
AddInSideAdapter.cs
using System;
using System.AddIn.Pipeline;
using CalcAddInViews;
using CalculatorContracts;
namespace CalcAddInSideAdapters
{
[AddInAdapter()]
public class CalculatorViewToContractAddInSideAdapter : ContractBase, ICalc1Contract
{
private Calculator _view;
public CalculatorViewToContractAddInSideAdapter(Calculator view)
{
_view = view;
}
public virtual double Calc(double a, double b)
{
return _view.Calc(a, b);
}
}
}
编译
前面7个文件,要分别编译成7个dll/exe文件,还好,每一只需要一个csc命令即可,写成一个.bat文件,如下
set libPath="C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.5" csc /target:library /out:Contracts\MyAddInContract.dll /lib:%libPath% /r:System.AddIn.Contract.dll;System.AddIn.dll AddInContract.cs csc /target:library /out:AddInViews\MyAddInView.dll /lib:%libPath% /r:System.AddIn.dll AddInView.cs csc /target:library HostAddInView.cs csc /target:library /out:AddInSideAdapters\MyAddInSideAdpter.dll /lib:%libPath% /r:System.AddIn.dll;System.AddIn.Contract.dll;AddInViews\MyAddInView.dll;Contracts\MyAddInContract.dll AddInSideAdapter.cs csc /target:library /out:HostSideAdapters\MyHostSideAdpter.dll /lib:%libPath% /r:System.AddIn.dll;System.AddIn.Contract.dll;HostAddInView.dll;Contracts\MyAddInContract.dll HostSideAdapter.cs csc /lib:%libPath% /r:System.AddIn.dll;HostAddInView.dll Application.cs csc /target:library /out:AddIns\CalcV1\MyAddIn1.dll /lib:%libPath% /r:System.AddIn.dll;System.AddIn.Contract.dll;AddInViews\MyAddInView.dll AddIn1.cs
结果
看看运行结果:
E:\CalculatorV1> application
Available Plugins:
[0]: MyAddIn1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
Which calculator do you want to use?
0
Input two numbers such as 2 3 (Or type <q> to exit).
%> 2 66
68
%> 333 221
554
%> q
878

被折叠的 条评论
为什么被折叠?



