您好,欢迎访问三七文档
当前位置:首页 > 电子/通信 > 综合/其它 > CWinForm编程(6)第36章插件
第36章插件插件可以在以后给应用程序添加功能。我们可以创建一个主机应用程序,随时间的推移给它添加越来越多的功能--这些功能可以是开发团队编写的,其他供应商也可以创建插件,扩展该应用程序。目前,插件在许多不同的应用程序上使用,例如IE和VisualStudio。IE是一个主机应用程序,它提供了一个插件框架,许多公司都使用这个框架提供查看Web页面时的扩展程序。ShockwaveFlashObject可以查看带Flash内容的Web页面。Google工具栏提供了特殊的Google功能,可以在IE中快速访问。VisualStudio也有一个插件模型,可以用不同层次的扩展程序扩展VisualStudio。定制应用程序总是可以创建插件模型,以动态加载和使用程序集中的功能。利用插件模型时,需要考虑许多问题。如何检测新的程序集?如何解决版本问题?插件可以改变主机应用程序的稳定性吗?.NETFramework3.5提供了一个框架,用程序集System.AddIn来保存和创建插件。这个框架也称为ManagedAddInFramework(MAF)。提示:插件还有其他称呼,如add-on或plug-in。本章内容如下:●System.AddIn体系结构●创建简单的插件36.1System.AddIn体系结构创建允许在运行期间添加插件的应用程序时,需要处理一些问题。例如,如何找到插件,如何解决版本问题,使主机应用程序和插件可以独立地升级。要解决这些问题,有几种方式。本节讨论插件的问题和MAF解决它们的体系结构:●插件的问题●管道体系结构●发现●激活●隔离●生存期●版本问题36.1.1插件的问题要创建一个主机应用程序,动态加载以后添加的程序集,必须解决几个问题,如表36-1所示。表36-1插件问题说明发现如何为主机应用程序查找新插件?这有几个不同的选项。一个选项是在配置文件中添加插件的信息。其缺点是安装新插件时,需要修改已有的配置文件。另一个选项是把包含插件的程序集复制到预定义的目录中,通过反射读取程序集的信息。反射的更多内容可参见第13章激活程序集动态加载后,还不能使用new运算符创建它的实例。但可以用Activator类创建这类程序集。另外,如果插件加载到另一个应用程序域中或新进程中,还需要使用不同的激活选项。程序集和应用程序域的更多内容可参见第17章隔离插件可能会使主机应用程序崩溃,读者可能见过IE因各种插件而崩溃的情况。根据主机应用程序的类型和插件的集成方式,插件可以加载到另一个应用程序域或另一个进程中生存期清理对象是垃圾回收器的一个工作。但是,垃圾回收器在这里没有任何帮助,因为插件可能在另一个应用程序域中或另一个进程中激活。把对象保存在内存中的其他方式有引用计数、租借和承办机制版本版本问题是插件的一个大问题。通常主机的一个新版本仍可以加载旧插件,而旧主机应有加载新插件的选项下面探讨MAF的体系结构,说明这个框架如何解决这些问题。MAF的设计目标如下:●应易于开发插件●在运行期间查找插件应很高效●开发主机程序应是一个很简单的过程,但不像开发插件那么容易●插件和主机应用程序应独立地升级36.1.2管道体系结构MAF体系结构基于一个包含7个程序集的管道。这个管道解决了插件的版本问题。因为管道中的程序集之间的依赖性很低,所以合同、主机程序和插件升级到新版本可以完全互不干扰。图36-1显示了MAF体系结构的管道。其中心是合同程序集。这个程序集包含一个合同接口,其中列出了插件必须实现、可以由主机程序调用的方法和属性。合同的左边是主机端,右边是插件端。图中还显示了程序集之间的依赖性。最左端的主机程序集与合同程序集没有依赖性,插件程序集与合同程序集也没有依赖性,这两个程序集都没有实现合同定义的接口,只是有一个对视图程序集的引用。主机应用程序引用主机视图;插件引用插件视图。视图包含抽象的视图类,该类定义的方法和属性与合同相同。图36-1图36-2显示了管道中类的关系。主机类与抽象的主机视图类有一个关联,并调用其方法。抽象的主机视图类由主机适配器实现。适配器在视图和合同之间建立连接。插件适配器实现了合同的方法和属性。这个适配器包含对插件视图的引用,把来自主机端的调用传送给插件视图。主机适配器类定义了一个具体的类,它派生自主机视图的抽象基类,实现了方法和属性。这个适配器包含对合同的引用,把来自视图的调用传送给合同。图36-2有了这个模型,插件端和主机端可以完全独立地升级了,只是需要使用映射层。例如,如果主机的一个新版本使用全新的方法和属性,合同就仍可以保持不变,只有适配器需要修改。也可以定义新的合同。适配器可以修改,也可以同时使用几个合同。36.1.3发现如何为主机应用程序查找新插件?MAF体系结构使用一个预定义的目录结构来查找插件和管道的其他程序集。管道的组成部分保存在这些子目录中:●HostSideAdapters●Contracts●AddInSideAdapters●AddInViews●AddIns除了AddIns目录之外,其他目录都直接包含管道特定部分的程序集。AddIns目录为每个插件程序集包含一个子目录。插件也可以保存在完全独立于其他管道组件的目录中。管道的程序集需要使用反射来动态加载,才能获得插件的所有信息。而且,对于许多插件而言,这还会增加主机应用程序的启动时间。因此,MAF使用一个高速缓存,来保存管道组件的信息。该高速缓存是由安装插件的程序创建的,如果主机应用程序有管道目录的写入权限,该高速缓存就由主机应用程序创建。给管道组件高速缓存的信息是调用AddInStore类的方法来创建的。Update()方法查找还没有列在保存文件中的新插件。Rebuild()方法用插件的信息重建完全二进制的保存文件。表36-2列出了AddInStore类的成员。表36-2AddInStore成员说明Rebuild()RebuildAddIns()Rebuild()方法为管道的所有组件重建高速缓存。如果插件存储在另一个目录下,就可以使用RebuildAddIns()重建插件的高速缓存Update()UpdateAddIns()Rebuild()方法重建管道的完整高速缓存,Update()方法只用新管道组件更新高速缓存。UpdateAddIns()方法只更新插件的高速缓存FindAddIn()FindAddIns()这些方法都使用高速缓存查找插件。FindAddIns()方法返回匹配主机视图的所有插件集合。FindAddIn()方法返回一个特定的插件36.1.4激活和隔离AddInStore类的FindAddIns()方法返回表示插件的AddInToken对象集合。使用AddInToken类可以访问插件的信息,例如名称、描述、发布者和版本。使用Activate()方法可以激活插件。表36-3列出了AddInToken类的属性和方法。表36-3AddInToken成员说明Name、Publisher、Version、DescriptionAddInToken类的Name、Publisher、Version和Description属性返回用特性AddInAttribute赋予插件的信息AssemblyNameAssemblyName返回包含插件的程序集名称EnableDirectConnect使用EnableDirectConnect属性可以设置一个值,主机程序应使用该值直接连接到插件上,而不使用管道的组件。只有插件和主机程序运行在同一个应用程序域,插件视图和主机视图的类型相同时,才能使用这个属性。该属性仍要求管道的所有组件都存在QualificationData插件可以用特性QualificationDataAttribute标记应用程序域和安全需求。插件可以列出安全需求和隔离需求。例如,[QualificationData(“Isolation”,”NewAppDomain”)]表示插件必须保存在新进程中。可以从AddInToken中读取这些信息,激活有特定需求的插件。除了应用程序域和安全需求之外,还可以使用这个特性通过管道传送定制信息Activate()插件用Activate()方法激活,利用这个方法的参数,可以定义插件是否加载到新应用程序域或新进程中。还可以定义插件获得的权限一个插件可能使整个应用程序崩溃,例如IE可能因一个失败的插件而崩溃。根据应用程序类型和插件的类型,可以让插件运行在另一个应用程序域或另一个进程中,来避免这个问题。MAF给出了几个选项。可以在新应用程序域或新进程中激活插件。新应用程序域还可以有有限的权限。AddInToken类的Activate()方法有几个重载版本,在这些版本中,可以传送加载插件的环境参数。表36-4列出了不同的选项。表36-4AddInToken.Activate()的参数说明AppDomain可以传送一个加载插件的新应用程序域,这样可以使插件独立于主机应用程序,还可以从应用程序域中卸载插件AddInSecurityLevel如果插件应使用不同的安全级别来运行,就传送枚举AddInSecurityLevel的一个值,其值可以是Internet、Intranet、FullTrust和HostPermissionSet如果预定义的安全级别不够安全,还可以给插件的应用程序域赋予PermissionSetAddInProcess插件还可以运行在与主机应用程序不同的进程中。可以给Activate()方法传送一个新的AddInProcess。如果所有的插件都卸载了,新进程就可以退出,否则新进程就继续运行。这个选项可以用KeepAlive属性设置AddInEnvironment传送AddInEnvironment对象是定义加载插件的应用程序域的另一个选项。在AddInEnvironment的构造函数中,可以传送一个AppDomain对象。还可以用AddInController类的AddInEnvironment属性获得插件的已有AddInEnvironment提示:应用程序域详见第17章。应用程序的类型也会限制可以使用的选项。WPF插件目前不支持跨进程。WindowsForms不能在不同的应用程序域之间连接Windows控件。下面列出调用AddInToken的Activate()方法时管道的执行步骤:(1)用指定的权限创建应用程序域。(2)用Assembly.LoadFrom()方法把插件的程序集加载到新的应用程序域中。(3)用反射调用插件的默认构造函数。因为插件派生于在插件视图中定义的基类,所以也加载了视图的程序集。(4)接着构造插件端适配器的一个实例。插件的实例传送给适配器的构造函数,使适配器能连接合同和插件。插件适配器派生于基类MarshalByRefObject,所以可以在应用程序域之间调用。(5)激活代码给主机应用程序的应用程序域返回插件端适配器的一个代理。插件适配器实现了合同接口,所以该代理包含合同接口的方法和实现。(6)主机端适配器的实例在主机应用程序的应用程序域中构造。插件端适配器的代理传送给该构造函数。激活代码会从插件令牌中查找主机端适配器的类型。主机端适配器返回给主机应用程序。36.1.5合同合同定义了主机端和插件端之间的界限。合同用一个接口来定义,该定义必须派生于基接口IContract。合同必须仔细考虑,因为它根据需要支持灵活的插件场景。合同没有版本支持,不能改变,所以插件以前的实现代码仍可以在新的主机程序中运行。新版本应通过定义新合同来创建。合同的类型有一些限制,其原因是版本问题,而且应用程序域要从主机应用程序跨越到插件上。类型必须是安全的,且支持版本,能在边界(应用程序域或跨进程)之间传送,也能在主机程序和插件之间传送。可以用合同传送的类型可以是:●基本类型●其他合同●可串行化的系统类型●简单的可串行化定制类型,包括基本类型、合同,以及没有实现代码的类型接口IContract的成员如表36-5所示。表36-5IContract的成员说明QueryContract()使用QueryContra
本文标题:CWinForm编程(6)第36章插件
链接地址:https://www.777doc.com/doc-2908311 .html