`
isiqi
  • 浏览: 16537986 次
  • 性别: Icon_minigender_1
  • 来自: 济南
社区版块
存档分类
最新评论
阅读更多

(一)插件技术原理及其特性

插件是一种遵循统一的预定义接口规范编写出来的程序,应用程序在运行时通过接口规范对插件进行调用,以扩展应用程序的功 能。插件在英文中通常称为plug-in、plugin或者plug in。插件最典型的例子是Microsoft的ActiveX控件和COM(Component Object Model,部件对象模型)实际上ActiveX控件不过是一个更高继承层次的COM而已。此外还有Photoshop的滤镜(Filter)也是一种比 较常见的插件。

关于ActiveX和COM

在Microsoft的.Net Framework 推出的之前(大约是2003年之前吧),ActiveX和COM可是炙手可热的技术啊!在那个年代,一个顶尖的VC++高手的标志是什么?是会COM编 程!不知道IUnknown接口和QueryInterface函数,你怎么可能通过Microsoft的MCSD认证考试?

现在当然不同了,我曾经见过不少断言COM和ActiveX已经消亡或终将消亡的文章。但是不管怎么说,个人认为,ActiveX和COM代表了插件技术的最高境界,通过对ActiveX和COM的研究,我们可以对插件有更深刻的认识。

关于ActiveX控件和COM技术的详细介绍,有兴趣的朋友不妨去“百度一下”,相信能够获得很多相关信息的。

插件技术过时了吗?

COM技术的逐渐淡出,使不少程序员产生了困惑:插件技术已经过时了吗?

NO!至少我不这样认为!毕竟,没有了插件技术,我们还有什么更好的方法为应用程序提供运行时的功能扩展呢?

COM的没落自然有其原因,例如编程实在是太复杂而难以掌握,还有就是在这个病毒和木马肆虐的年代,其安全性也令人堪忧。但至少我们可以看到,插件技术的成功应用还是有的:比如PhotoShop的滤镜,比如各大主流工控软件的功能扩展。

对于插件的理解,我们应该注意以下几点:

一、插件是遵循统一的预定义接口规范编写的。

下面我以微软的ActiveX Control Test Container为大家解释一下插件的接口定义。

1、打开VS.NET 2005;

2、点击主菜单的“工具”菜单;

3、如果在“工具”菜单中未找到“Activex Test Container”,则点击“工具”-〉“外部工具”,在“外部工具”对话框的“菜单内容”列表框中,选中“Activex Test Container",然后点击右上角的"添加“按钮,再点击左下角的”确定按钮“,此步骤将在在“工具”菜单中添加“Activex Test Container”菜单项;

4、点击主菜单的“工具->Activex Test Container”菜单项,运行”Activex Test Container" ;

查看更多精彩图片

5、在“Activex Test Container”程序中点击“编辑->插入新控件”菜单;

6、在“插入控件”对话框中找到“日历控件 11.0" 控件,点击确定;

查看更多精彩图片

7、点击“控件”-〉“调用”方法,弹出“调用方法”对话框,按下图所示的方法将日历控件的背景色设置为红色;

查看更多精彩图片

在这里,“BackColor”是一个预定义的通用的Activex控件接口函数,用于设置控件的背景颜色。

二、应用程序是在运行时调用插件以实现功能扩展的

插件最吸引人的地方当然就是其所实现“运行时(run-time)"功能扩展。这意味着软件开发者可以通过公布插件的预定义接口规范,从而允许第三方的软件开发者通过开发插件对软件的功能进行扩展,而无需对整个程序代码进行重新编译。

运行时(run-time)是相对于编译时(assembly-time)而言的。一般来说,软件开发者对软件功能更新时,是在源代码级别进行更新,然后 对整个程序或部分动态链接库(DLL)进行重新编译,进而发布应用程序的新版本,这就是编译时(assembly-time)的软件更新。

三、插件技术的优缺点

运行时的软件功能扩展其优点是显而易见的:

1、对软件的开发者而言,只需对主程序和某些常用插件进行更新和维护,然后通过公布插件接口吸引第三方的软件开发者对主程序的功能进行扩展,这是一种“我为人人,人人为我”的双赢策略;

2、对最终用户而言,可以通过有选择地购买第三方提供的插件实现自己所需要的功能,从而实现最佳性价比组合,以节省不必要的开支。

但是,运行时的软件功能扩展也有其弊端:

1、为实现运行时的软件扩展,程序开发者必须编写更多、更复杂的代码,从而会对程序的执行效率产生一定的影响。关于这一点,我会在第二讲中详细论述;

2、由于插件是在运行时加载的,因此第三方插件可能对用户造成危害。这种危害通常可以分为两类:

(1)由于插件开发者的技术水平原因导致的插件BUG,这种BUG可能导致内存泄露、死机、数据丢失等等故障,从而影响到用户对软件的使用;

(2)插件开发者恶意开发类似于病毒和木马的插件,窃取或毁坏用户数据,使用户遭受不必要的损失;

为了避免此类缺点,软件开发者可能需要付出额外的代价,如需要对第三方插件进行检验和认证,或者干脆不对外提供插件开发接口,仅由自己提供插件。

(二)用C#编写插件

为了使大家对插件有更深入的了解,让我们先重温一下通常情况下创建和调用DLL的过程。

每一个程序员都知道,我们应当将某些类或者模块编译为DLL,然后在主程序中调用,关于这样做的目的和好处,我就不再啰嗦了。

假设我创建了一个名为TirayComm.dll的类库,并编写了一个UDP类用于UDP数据传输:

namespace Tiray.Net
{
public class UDP
{
...
//port--本地侦听端口
//ttl时间,以毫秒为单位
public UDP(int port,short ttl)
{
...
}

//初始化
public void Init()
{
...
}

//发送数据
//data--待发送数据
//remoteIP--远端IP地址
//port--远端UDP端口
public void Send(byte[] data,string remoteIP,int port)
{
...
}
//关闭
public void Close()
{
...
}

}
}

我在主程序中创建UDP类的实例和调用UDP类方法的代码如下:

using Tiray.Net
...
Tiray.Net.UDP udp=new UDP(25000,2000);
udp.Int();
...

别忘了要先在项目中添加对TirayComm.dll的引用。

我先提醒大家一下,上面的步骤都是在你的IDE中完成的,也就是说是在你编写程序代码的过程中完成的。

下面我用我以前开发的一个软件项目为大家详细解释一下用C#实现插件的一些技术细节。

我曾经接到过一个SP的软件项目,为一家电台开发一个听众短信互动平台。在需求分析过程中,我意识到电台的短信互动平台需要非常灵活的功能扩展,因为一个 电台往往有好几个频率,每个频率平均每个小时都是一个不同的节目,每一个节目对短信平台的要求都可能不同:有的节目仅仅要求短信平台将听众的短信显示给主 持人;有的节目要求短信平台能够自动回复某些信息;有的节目如短信答题等要求短信平台能自动判断用户的答案是否正确,并能提供抽奖功能...... 而且最糟糕的是即使同一个节目,其内容也随时有可能更换或改版,然后对短信平台提出新的要求。

这时候我开始有了编写插件的想法。我希望当主持人提出一个新的短信应用要求时,我只需要编写一个插件来实现相关的应用逻辑,然后将插件安装到短信平台的特 定目录下,就可以实现相应的功能,而无须对整个短信平台进行升级。同样,当主持人不再需要某个短信应用的时候,我只须简单地从特定目录中将相应的插件删除 即可。

下面是我编写的插件的基类,请注意这是一个抽象类。从插件的意义说,其中的两个公共抽象方法和一个事件就是插件的接口规范定义。我没有用 interface关键字来定义插件的接口,是因为我还有一些与短信网关有关的代码需要在这个类中实现,而且我也不想考虑在插件中实现多重继承的问题。实 际上,也可以使用interface关键字来定义插件的接口。关于抽象类和接口的有关内容,大家可以到MSDN里寻找,我就不多说了。

namespace Tiray.SMS
{
public abstract class Plugin
{
//插件的名称
protected String pluginName;
...
//OnReceive是接收到中国移动短信网关类CMPP30和中国联通短信网关类SGIP短信后的事件处理函数
//destNumber--SP端的号码,如1066123456
//phoneNumber--手机号码,如13812345678
//Message--短消息
public abstract void OnReceive(String destNumber,String phoneNumber,String message);

//显示插件属性,用户可以调用此方方法察看和设置插件属性
public abstract void ShowProperty();
...
//从内存中销毁插件实例时的处理函数。
public abstract void Finalize();
...
//插件的事件
//通常是一个发送短消息请求
public event PluginEventHandler PluginEvent;
}
//插件事件定义
public delegate void PluginEventHandler(object sender, PluginEventArgs e);

}

下面是一个为交通类节目写的交通违法查询的短信插件实现。这个插件的作用是当用户发送一个车牌号到短信互动平台时,可以返回该车牌号在一段时间内的交通违法记录。这个插件被编译为一个名为Illegal.dll的类库,然后发布到主程序的“Plugin”子目录下。

using Tiray.SMS;
namespace Tiray.SMS.RadioService
{
//交通违法查询短信插件
public class IllegalQuery:Plugin
{
...

public IllegalQuery()
{
...
pluginName="交通违法行为查询";
...

}
public override void ShowProperty()
{
...
}
public override void Finalize()
{
...
}

public override void OnReceive(String destNumber,String phoneNumber,String message)
{
...
//对接收到的车牌号进行检验和处理
string carNumber=CheckCarNumber(string message);
if(carNumber!=string.Empty)
{
...
//查询违法记录并发送到用户手机
QueryAndSend(carNumber,phoneNumber);
...
}
...
}

}
}

从上面的代码来看,我只是实现了一个很普通的类继承,似乎看不出插件有什么与众不同的地方。下面来看一看在主程序中是如何调用插件的。

...
using Tiray.SMS;
using System.Reflection;

...

...
protected Hashtable n_htPlugin=null;
...
protected void InitPlugin()
{
m_htPlugin=new Hashtable();

string dir=Directory.GetCurrentDirectory()+@"\plugin";
string[] files=Directory.GetFiles(strDir,"*.DLL");

foreach(string file in files)
{
Tiray.SMS.Plugin plugin=null;
try
{
System.Reflection.Assembly asm=System.Reflection.Assembly.LoadFile(files);
Type[] types=asm.GetTypes();
foreach(Type type in types)
{
if(type.BaseType.FullName=="Tiray.SMS.Plugin")
{
plugin=asm.CreateInstance(type.FullName) as Plugin;
break;
}
}
}
catch(Exception ex)
{
...
plugin=null;
...
}
if(plugin!=null)
{

plugin.PluginEvent+=new PluginEventHandler(OnPlugin);
m_htPlugin.Add(plugin.Name,plugin);
}
}
}

//来自插件的消息
//通常是一个发送短消息请求
protected void OnPlugin(object sender,PluginEventArgs e)
{
...
}

}

请注意插件实例是使用Sytem.Reflection.Assembly类的CreateInstance方法 创建的。对比前面我提到过的UDP类的实例创建方法,我并没有在主程序中添加对Illegal.DLL的引用,程序中也没有直接对 IllegalQuery类进行声明的代码。

在上一讲中,我提到过,插件的一个优点就是运行时的功能扩展,通过上面的代码,大家应该对插件的这一特性有一些了解了吧。

上面的只是一个很简单的例子,但是大家也已经看到,为了实现插件,我不得不在主程序中添加了更多的代码,如我创建了一个Hashtable用于在内存中保 存每一个插件实例;我使用了比常规的类实例创建方法更多的代码来创建插件实例;虽然我没有列出插件事件处理函数的代码,但是可以想象,我一定要使用更多的 代码来判断当前的事件是由哪一个插件引发的。实际上,插件的实现还需要考虑一些更复杂的问题,在第三讲中,我会对插件进行更深入的探讨。

现在,让我们回顾一下插件的两个基本特性及实现方法:

统一的调用接口

这是用abstract关键字定义一个抽象类或者用interface关键字定义一个接口来实现的;

运行时加载

这是使用System.Reflection.Assembly类的相关方法来实现的。

分享到:
评论

相关推荐

    Dynamic Parameter Plug-in

    动态参数插件(Dynamic Parameter Plug-in)是Jenkins持续集成工具中的一个重要扩展,它为构建过程提供了更加灵活的参数化选项。在 Jenkins 中,参数化构建允许用户在触发构建时输入特定值,这些值可以影响构建的过程...

    NVIDIA Photoshop Plug-ins插件

    NVIDIA Photoshop Plug-ins插件是专门为Adobe Photoshop设计的一款扩展工具,它极大地拓宽了Photoshop的功能范围,特别是针对游戏开发和图形设计领域的用户。这款插件的主要功能在于支持DDS(DirectDraw Surface)...

    jenkins的Dynamic Parameter Plug-in 插件

    Jenkins的Dynamic Parameter Plug-in是一款强大的插件,它允许用户在构建过程中动态地定义和配置构建参数。这个插件增加了构建的灵活性,可以根据不同的条件或输入来决定构建时的参数,提高了持续集成/持续部署(CI/...

    PCI Hot-Plug Specification Revision 1.1.pdf

    PCI Hot-Plug Specification Revision 1.1 PCI Hot-Plug Specification Revision 1.1 是PCI-SIG(Peripheral Component Interconnect Special Interest Group)发布的一份技术规格文档,旨在定义热插拔(Hot-Plug)...

    RCP+Plug-in开发自学教程_RCP+Plug-in开发自学教程_源码

    **RCP+Plug-in开发自学教程** 在软件开发领域,Eclipse RCP(Rich Client Platform)和Plug-in机制是构建可扩展、模块化桌面应用程序的重要工具。本教程将引导你深入理解并掌握这两种技术,帮助你成为一名熟练的RCP...

    jquery_plug插件

    **jQuery Plug插件详解** jQuery Plug是一款基于JavaScript库jQuery的扩展插件,它为开发者提供了更丰富的功能和更便捷的编程体验。这款插件尤其适用于那些希望增强网页交互性、优化用户界面的开发者。在本篇文章中...

    GNU ARM Eclipse Plug-in

    《GNU ARM Eclipse Plug-in:为Eclipse开发环境增添ARM支持》 GNU ARM Eclipse Plug-in是专为Eclipse集成开发环境(IDE)设计的一款扩展插件,由开发者ilg-ul和justxi共同贡献。该插件的主要目的是为了在Eclipse...

    Home Plug_AV标准

    Home Plug AV标准是由HomePlug电力线联盟制定的电力线通信标准,其具体版本为1.1版,发布于2007年5月21日。该标准的完整标题是《HomePlug AV Specification Version 1.1》。根据描述,这个标准是电力线通信(PLC)...

    高德PLUG417机芯资料

    "高德PLUG417机芯资料" 高德PLUG417机芯是高德标准型红外热成像机芯,专门为机芯集成与二次开发应用提供相关信息与技术支持。该机芯具有高分辨率、耐冲击、振动、扩展性强等特点,适用于电力监测、工业自动化、安防...

    vim plug taglist cscope

    总的来说,"vim plug ubuntu" 指的是在基于 Ubuntu 的 Linux 发行版上利用 Vim Plug 管理插件,而 "vim plug taglist cscope" 则是在这个环境中安装并使用 TagList 和 Cscope 插件,以提升代码编辑和浏览的体验。...

    CarPlay Communication Plug-in R14G17_carplay_Carplayplugin

    CarPlay Communication Plug-in R14G17_carplay_Carplayplugin是一款专为苹果CarPlay系统设计的通信插件,其主要功能是增强车辆与iPhone之间的连接性和交互性。这款插件是苹果公司针对车载信息系统的一项重要更新,...

    Dynamic Parameter plug-in Jenkins plug-in

    "Dynamic Parameter plug-in"是 Jenkins 插件家族中的一个重要成员,它为构建过程提供了更灵活的参数化构建功能。由于某种原因,这个插件在官方仓库可能无法直接搜索到,但它的价值在于能够帮助开发者自定义构建过程...

    什么情况下插入C-PLUG卡或者C-PLUG有什么作用?.pdf

    C-PLUG卡是西门子自动化和驱动产品中用于数据存储和配置的插件模块,它在特定情况下起到至关重要的作用。C-PLUG卡通常在西门子的PLC(可编程逻辑控制器)和其他自动化设备中使用。以下是在什么情况下需要插入C-PLUG...

    CarPlay Communication Plug-in R14G17.2.zip

    CarPlay Communication Plug-in R14G17.2 是苹果公司为开发者提供的一个核心组件,用于构建与CarPlay系统交互的应用程序。这个插件是官方发布的,确保了开发者能够遵循苹果的安全标准和用户体验规范,为iOS设备用户...

    vim-plug, 我的vim插件, 基于vim-plug安装.zip

    Vim-plug是一个轻量级且高效的 Vim 插件管理器,它允许用户方便地安装、更新和管理各种Vim插件。这个压缩包“vim-plug, 我的vim插件,基于vim-plug安装.zip”包含了Vim-plug的源代码,这将帮助你了解如何在你的Vim...

    Fat Jar Eclipse Plug-In教程.

    ### Fat Jar Eclipse Plug-In教程详解 #### 一、Fat Jar Eclipse Plug-In简介 **Fat Jar Eclipse Plug-In** 是一个非常实用的Eclipse插件,它能够帮助开发者将Java项目打包成一个包含所有依赖的可执行JAR文件,即...

    Plug and Play BIOS Specification

    1.1 Goals of a Plug and Play System BIOS 4 1.2 Enhancements to the current BIOS architecture 5 1.3 Elements of the Plug and Play BIOS architecture 6 1.3.1 Bi-modal functionality 6 1.3.2 OS ...

    plug插件设计实例

    本文将深入探讨“plug插件设计实例”,并结合提供的压缩包文件“test_plug-0.1”来讲解插件设计的基本概念、架构以及实现步骤。 插件设计的核心是模块化和可扩展性。它允许开发者将复杂的应用程序分解为独立的、可...

    PCI Hot-Plug Specification v1.1

    PCI Hot-Plug Specification v1.1版本于2001年6月20日发布,它是对PCI热插拔规范的一个修订版。本次修订主要加入了对PCI 2.2标准、PCI电源管理、PCI-X标准和SMBus(系统管理总线)的支持。PCI热插拔规范的制定和更新...

    Motion LIVE Plug-in 1.02.1203.1.rar

    Motion LIVE Plug-in 1.02.1203.1 是一款专为增强动态图形设计体验而开发的插件,适用于那些希望在实时环境中创作、编辑和展示动态视觉效果的专业人士。这款插件可能与视频编辑软件或三维动画软件如Adobe After ...

Global site tag (gtag.js) - Google Analytics