其实我也常看到类似问题,我也回答过,说来也不难,反射,主程序提供一个固定的调用方法的功能,dll在加载后,能够提供一些函数,让主程序调用,加载到程序中,剩下的就各不相关了。比如:
App伪代码
public bool Load(string file)
{
Assembly asmb = Assembly.LoadFrom(file);
foreach(var c in asmb.GetTypes())
{
var enter = c.GetMethod("enter");
if(enter == null) continue;
enter.Invoke(...);
return true;
}
return false
}这样,插件模块中,只要提供了enter,就可以被加载。就类似程序的main被启动一样了。或是提供一个dll,里面定义了所有的interface,程序使用这个dll中的接口,模块也引用这个dll,提供实现。这些可能都可行,又可能有问题。我没实际做过类似的功能。所以想请朋友们给些建议。另外,软件如果插件化,从逻辑上,插件本身需要低耦合,那么,这势必增加了主程序和插件之间的通讯难度?不知道我这样理解是不是因为我的设计不合理造成的。比如,我对A说,你去吧你的车开到某处接个人来。对B说,你开公司的车道某处接个人来。
这里,我知道A有车,B没车。所以我直接可以这样操作。
但如果接口化了,我就必须说:
找一个人来,判断一下这个人是否有车,根据结果选择一个操作去执行。这样就会复杂一些了。是不是?
意思是逻辑上,接口、插件的设计是更高一级的抽象,是否就一定的带来更多的逻辑复杂性呢?
在复述一下2个问题,免得我描述不清晰造成大量无意义回复:
1.插件化的软件,是如何架构的?请有经验的给些意见,建议。
2.什么功能适合插件化?是否必然的会带来逻辑复杂度上升?如何避免设计过度?
App伪代码
public bool Load(string file)
{
Assembly asmb = Assembly.LoadFrom(file);
foreach(var c in asmb.GetTypes())
{
var enter = c.GetMethod("enter");
if(enter == null) continue;
enter.Invoke(...);
return true;
}
return false
}这样,插件模块中,只要提供了enter,就可以被加载。就类似程序的main被启动一样了。或是提供一个dll,里面定义了所有的interface,程序使用这个dll中的接口,模块也引用这个dll,提供实现。这些可能都可行,又可能有问题。我没实际做过类似的功能。所以想请朋友们给些建议。另外,软件如果插件化,从逻辑上,插件本身需要低耦合,那么,这势必增加了主程序和插件之间的通讯难度?不知道我这样理解是不是因为我的设计不合理造成的。比如,我对A说,你去吧你的车开到某处接个人来。对B说,你开公司的车道某处接个人来。
这里,我知道A有车,B没车。所以我直接可以这样操作。
但如果接口化了,我就必须说:
找一个人来,判断一下这个人是否有车,根据结果选择一个操作去执行。这样就会复杂一些了。是不是?
意思是逻辑上,接口、插件的设计是更高一级的抽象,是否就一定的带来更多的逻辑复杂性呢?
在复述一下2个问题,免得我描述不清晰造成大量无意义回复:
1.插件化的软件,是如何架构的?请有经验的给些意见,建议。
2.什么功能适合插件化?是否必然的会带来逻辑复杂度上升?如何避免设计过度?
这个插件很强大了
主程序给插件dll提供一些公用的虚接口和类,界面上一切本插件相关的东西都由dll自己提供,例如ICON,按钮操作等,主程序专门做个DLL管理插件
你不提供也没无谓。
反正有钩子。
MEF 像DI 多一些,和 Add-In 模型 划分的很清楚。
这里有篇文章可以参考下的 http://www.cppblog.com/kenlistian/archive/2009/03/11/47753.html
这样做的前提就是要先定义哪些地方需要开放给插件。
个人觉得这里的插件化机制挺不错的。
我当时的想法是这样的,主界面是个winform,上面有个MenuStrip,每一个MenuStripItem代表一个插件的功能。Winform的下部是个TabControl,当点击MenuStripItem下的子MenuStripItem时,就在TabControl里增加一个TabPage。首先我定义了两个接口:using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;namespace Contracts
{
public interface IAddIn
{
string AddInName { get; }
IList<ITool> Tools { get; }
} public interface ITool
{
string ToolName { get; }
Control UserInterface { get; }
IList<ITool> SubTools { get; }
}
}IAddIn代表插件,ITool代表该插件下拥有的功能。然后插件实现这个接口,并生成一个winform类库,放到指定的一个目录下,然后我在工具的主程序里遍历这个目录,找到所有dll,反射看有哪些类实现了IAddIn接口,把它们加载到MenuStrip里: void LoadAddIns()
{
string dir = Application.StartupPath + "\\AddIns\\";
if (!Directory.Exists(dir))
Directory.CreateDirectory(dir);
foreach (string d in Directory.GetDirectories(dir))
{
foreach (string s in Directory.GetFiles(d))
{
if (Path.GetExtension(s) == ".dll")
{
try
{
//获取程序集,获取程序集下所有类,判断是否实现了IAddIn接口
Assembly ass = Assembly.LoadFile(s);
foreach (Type t in ass.GetTypes())
{
if (Array.IndexOf(t.GetInterfaces(), typeof(IAddIn)) > -1)
{
IAddIn c = Activator.CreateInstance(t) as IAddIn;
ToolStripMenuItem item = new ToolStripMenuItem(c.AddInName);
item.Tag = c;
BuildTree(item, c.Tools);
menuStrip1.Items.Add(item);
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
}
}
iModel是由我公司开发的一款软件快速开发及运行平台。该平台所应用的方法是一种模型驱动的软件开发方法,它包括建模工具和模型运行引擎 ,建模工具用于设计和维护软件的基本模型和插件模型,模型引擎用于执行软件模型生成软件。iModel在通常的基本模型上增加了可用插件扩展其功能的接口。iModel的插件不同于一般意义上的插件,它是一种可以带参数的进一步扩展基本模型功能的可复用的软件组件。插件必须挂在基本模型对象上(也就是插件的主体对象),让主体对象实现一定的业务规则或者完成一定的功能。插件可以自动响应主体对象的消息并完成相关操作。
iModel通过为插件建模定义把易变的插件应用和相对稳定的插件实现相分离,以实现一种能软件系统在运行态就可以通过对插件参数的调整而改变插件的内部运行状态的能力。
iModel为软件开发者提供丰富的通用插件模型和插件实现,覆盖软件界面、功能,结构,流程等各方面;通过基础建模和大量插件的选择组装与集成可以实现软件系统的复杂逻辑功能和丰富界面表现。用户也可以很容易地在系统中增加插件模型及其实现完成对特殊功能的业务功能。
iModel提供从建模、模型维护一整套工具及运行平台。不同角色的开发人员可以同时在此平台进行分工协同开发。iModel可以在运行态流畅地修改模型、调整插件并可以直接看到运行的效果。开发、运行、维护完全以模型为中心
不过我觉得那个太复杂.以前设计过类似的东西.不过那是还在学校的时候的实验品.
代码已经找不到了囧.所以我只能描述一下了:首先插件和插件的宿主方有自定的接口.
插件和宿主之间的通信通过接口定义实现沟通.`每一个插件都是一个类.(不要使用你例子里的伪代码那
样的Invoke,那样除去设计上的糟糕不说,性能也会大打折扣)系统预定义了一系列的接口和类.用于描述dll中的插件.
插件信息的描述使用CustomAttribute 的方式实现.
可以使用反射获取这些信息生成一个PluginInfo.
异或是由插件自己实现PluginInfo接口,来动态的生成
插件信息(这样插件就能由.net dll 扩充至dll载入的脚本)
插件本身使用一个PlugInAttribute来标识.系统提供以下的服务:
插件dll的载入.使用反射遍历程序集里的类,将使用PluginAttribute
标识的类找出来放入管理器.插件的注册.将一个type作为插件注册到管理器插件类的创建(插件类工厂)由插件管理器中创建出对应Type的实例系统维护一个插件管理器.可以对当前系统载入的插件进行查询和添
加删除的集中管理.它实质上是一个key-value的映射.其中key 称为插
件名.由PluginAttribute描述.value是一个delegate.系统默认提供一
个将插件类实例化的delegate.但如前所说,完全可以由自己实现PluginInfo
的方法来达到自定实例创建过程的目的.当classfactory收到创建一个
名为name的实例的时候,它会检索到对应的delegate调用并将返回的
对象回送给调用方.呃...大概就是这么个东西.整个系统就是一堆类工厂和命令模式的实现.
最后写完后发现用处不大.而且宿主开发很烦,必须使用面向接口的设计.只有在写复杂架构的东西的时候才能体现出这样开发模式的优点.平时
写点小项目小玩具什么的用这玩意纯属蛋疼.
感觉能用,但不太实用。小程序的话,直接做成一个程序更方便。大程序的话,这种方式也不太可靠,很有可能有的插件Dll没有遵守某些规定,导致程序崩溃或运行不正常。
可能互不熟悉的团队独立开发,由必须整合在一起的时候会有用。比如C公司让A公司开发一部分功能,B公司也开发一部分功能,最后要整合在一起成为一个软件。但A公司和B公司互相不交流技术,这时可能会有点用吧。
比如Lua, 51lua.dll 就可以配合上C#。
你只需要给他提供接口就OK,至于一些小逻辑就不需要你操心了,这是双赢。例:
接口:GetFileName();
Copy(x,x);
那lua自己只需要做一个简单的判断
x = GetFileName();
if x ~= "" then
Copy(x,x);
end这样C#的压力适当的减轻,你只需要判断如果文件不存在返回一个 空字符串或者null等,这就要自己去定义接口了
这是我做的一个vs的插件。你想为别的软件做插件。方法很多。
留接口,只是为了扩展方便罢了。
Windows平台下C++插件系统实现的几个关键技术问题及其解决思路 开源的话看看Firefox的实现吧。
http://www.steinberg.net/en/company/developer.html
每个插件可以用tiemer控制,系统启动时加载后,取到任务就工作。
没有任务就隔几秒找找。
有新的组件加进来,改改配置文件即可。
比较典型的有鼎鼎大名的office,支持VBA调用,还支持dll的“嵌入”!
当然,好点的商业软件如autocad等等,或者说支持二次开发的软件都是这种架构!
但是,这是比较“古老”的技术,从90年代初ole到90年代中期(win95)com结构的成熟,后来几乎就没有什么改变,最近两年多了python,但是没有com它什么也干不了(不绝对啊,别较劲)!
关于com的中文书,有几本奉为“宝典”的,其实真是垃圾,王道还是msdn,呵呵!
最后,近5年com几乎被人淡忘了,但是都在用,也说明这种架构成熟了!就如汇编,还有人提吗?
二、使用Visual studio中的插件向导,可以很快开发出一个插件;
三、注意插件构成;插件的文件构成;
四、插件的部属机制;
改天我在我的博客中详细进行说明(最近有些,要开发的软件太多);
但是缺乏开发文档和资料,不知道哪位大侠,有相关的信息,提供一下。http://topic.csdn.net/u/20110605/22/62c93d47-b744-440c-a1e5-b7b1bf2ed32a.html
1.定义配置文件记录插件信息,里面记录所有插件信息,举个简单例子就是
<Service Base="XXX.XXX.Services.IUserSecurity" Type="XXX.XXX.Services.UserSecurity,XXX.XXX" />
里面的IUserSecurity及自己定义的插件接口,我们根据插件运行行为分为两类,初始化后直接运行的插件与初始化后等待用户调用的插件。比方初始化后等待用户调用的插件接口,我是这样定义的
public interface IService
{
void Initialize();
void Unload();
event EventHandler Initialized;
event EventHandler Unloaded;
}
并自己书写了所有插件的基类Service,所有服务需集成此基类。插件类重载initialize实现自己插件初始化代码,也需要重载unload方法提供服务卸载功能写自己插件框架的服务提供类ServiceProvider,他需具有
1.实现服务容器,与操作方法,为了方便可以实现IServiceContainer接口
2.实现解释自己定义的服务配置文件的方法,根据配置文件将服务加入服务容器
问题是:如何将插件的DLL输出,自动向脚本公开,脚本引擎可以自动识别用户插件中提供的功能接口。