学习.NET:NET组件是什么

学习.NET:NET组件是什么
学习.NET:NET组件是什么

.NET组件是什么

组件的定义有多种,但最常见有几种。组件是可互换的软件部分,它既是工业化系统的产物,也是工业第系统的动力。在.NET平台的组件层中,组件是以Assemblies的形式创建的。

.NET平台创建了组件,并将组件作为其基本的元素。从本质上看,.NET平台组件是一个用任何.NET语言以插件形式开发的可互换的软件部件,它可以与其他应用程序实现互操作。使用COM+服务的.NET组件被称作服务化组件,以示与.NET中标准的可管理组件的区别。

强命名.NET组件

下面我们将讨论强命名组合体(.NET组件)是什么。当开发可配置类时,它必须被编译。在编译代码后,有二方面的事情需要考虑。第一,COM+集成要求被编译的组合体必须被强命名。我们必须通过运行被称为sn.exe的强命名工具生成一个密码,以生成一个强命名的组合体。一旦编译了强命名的组合体,必须使用System.Reflection名字空间中一个被称作AssemblyKeyFileAttribute的组合体级的属性调用存储在文件中的该密码。

#using

using System;

using System::EnterpriseServices;

using System::Reflection;

[assembly: ApplicationName("FirstApp")]

[assembly: ApplicationActivation(ActivationOption.Library)]

// AssemblyKeyFile属性调用由sn.exe生成的密码文件,组合体将拥有强命名

[assembly: AssemblyKeyFile("thiskeyfile")]

namespace ESExample

{

???

}

第二,在编译强命名的组合体时,必须调用输出System.EnterpriseServices名字空间中类型的组合体━━System.EnterpriseServices.dll。下面是生成密码和编译可配置类的命令:sn -k thiskeyfile

Cl /out:ThisExample.dll /t:library

/r:System.EnterpriseServices.dll FirstCfgClass.cpp

在COM+中注册.NET组件

COM+有二种注册方式:动态方式和手工方式。这二种方式都相当简单,但对于本例,动态方式是合适的。动态注册方式还有一些要求:

1、组合体必须是强命名的。

2、组合休可以不在全局性的组合体缓冲区中。

3、组合体必须被可管理的(.NET)客户端使用。

4、组合体激活类型必须是Library。

初看起来,似乎限制相当严格,但其实它包括多种情况。读者一定在想,激活类型必须

是Library,但还没有创建过Library COM+组件呢。在.NET中,服务组件的客户端在同一台计算机上,或者客户端将远程访问代理应用程序,以访问COM+组件。因此,纯.NET解决方案将在大多数情况下使用动态注册方式。

在客户端第一次实例化服务化组件时,就会进行动态注册,而且对于每个版本的组合体而言,只会注册一次。我们需要注意COM+目录更新和组件第一次被访问之间在时间上的滞后。代码将跟踪内存中对象的数量以及在一定的活动期间及其之后仍然有多少对象仍然是活动的。

注意,在对对象进行初次调用后,系统中存在一个有5个对象的缓冲池。似乎是一旦一个对象被实例化,在有方法被调用之前,它一直是活动的。一旦有方法被调用,该对象就只在调用期间是活动的。这也提醒我们,在准备使用对象之前,尽量不要去招惹它们。

移植的策略

在决定将部分或全部现有的应用软件移植到.NET环境中,就需要决定哪种移植方法最适合你。本篇文章介绍了水平移植和垂直移植二种应用软件的移植方式。

水平移植和垂直移植

水平移植是指取代应用程序中的全部一个层。例如,可以选择取代基于Web的表示层中的ASP代码或选择取代中间层中的COM代码。垂直移植指的是替换一个应用程序中所有n层中的一部分。

组件设计

本篇文章提出了一些与向.NET/COM移植和组件设计互操作性问题方面的普遍原则。通过互操作层在.NET和COM环境之间进行互操作时,CCW或RCW(依据调用的方向而不同)必须在二个环境之间的调用栈中对数据进行转化,有些数据类型无需转换。包括整型、长整型和浮点型数据类型在内的通用性数据无需转换,而非通用性数据则需要转换。

Visual Basic的BSTR是非通用性数据类型的一个例子。在向.NET移植应用程序之前,应该在可管理性和非可管理性代码之间尽量少地使用非通用性数据类型,原因是相关的转换代价将影响到应用程序的性能。

通用数据类型

大多数的数据类型在可管理性和非可管理性内存中的表示都相同,互操作层无需作特别的处理,由于在可管理和非可管理代码之间无需转换,因此这些数据类型被称作通用类型。整型和浮点型数据是通用类型数据类型,由通用型数据类型组成的数组和结构也是通用型数据类型。

非通用数据类型

非通用数据类型在可管理和非可管理语言中的表示是不同的,由于当在可管理性和非可管理性代码之间进行互操作时,它们要求互相转换,因此被称作非通用型数据类型。例如,由于有几种不同的非可管理性表示,其中的一些可能需要进行转换,因此,可管理的字符串是非通用型数据类型。字符串、日期、对象是非通用型数据类型的例子,在执行互操作时,它们都需要转换。

现有的COM组件和可管理客户端

在向.NET平台移植应用程序时,需要考虑现有的COM组件所使用的界面。尽管会不再使用现有的COM客户端,仍然会在.NET客户端中使用现有的COM组件。因此,在设计界面时,应该如何既考虑到二种环境中现有的组件也要考虑到未来的可管理客户端。

在向.NET移植组件时,需要使用tlbimp工具自动地生成一个应用程序的RCW。缺省情况下,RCW使用与现有组件相同的界面(通过定义相同和属性和方法)。在许多情况下,传统的COM类型的界面并不是天生地从可管理性代码中使用的。可管理性代码开发人员将能够充分地利用下面的特性:

?参数化的构造器

?继承

?静态方法

我们应当考虑编写一个COM对象使用的自定义包装类,向可管理客户端提供这些能力,创建更适合可管理代码环境的界面。包装类在内部使用COM组件的RCW,并代理对现有COM组件的大多数调用,一些调用能够完成更复杂的数据类型转换工作,例如,ADO .NET 数据集和ADO记录集之间的映射。此后,我们可以将更多的功能从COM组件转移到包装类中,而不会影响可管理代码。在决定是使用RCW或自定义的可管理性包装类时,有许多因素需要考虑。需要注意的是,TlbImp工具能够将COM类型库转换为.NET架构元数据。一旦类型库被转换为元数据,可管理客户端可以无缝地调用COM类型。为了简化使用,我们总是在类型库中提供类型信息。

如果组件有大量的已经习惯了现有对象模型的客户端,创建RCW和使用现有的界面就是一个比较合适的策略。Excel中的对象模型被使用VBA的Excel开发人员广泛使用,对象模型是高度结构化的,并且能够很好地映射由Excel提供的特性和用户界面。客户非常熟悉现有的对象模型,如果对象模型发生大幅度的变化,用户就需要进行大量的训练。在本例中,使用标准的RCW可能是合适的。

在编写COM界面的自定义的可管理包装类时,这些界面将被通过RCW从可管理性代码中调用,每个属性调用都需要有互操作层的参与,会带来一定的代价。对于一个只作很少工作的简单界面来说,互操作造成的代价将是30-50条汇编指令。对于完成大量工作的方法而言,这点代价是微不足道的,但对于一个简单的属性访问而言,这一代价还是太大了。

如果在不久之后要将COM组件的客户端移植到.NET平台上,在考虑编写自定义的可管理包装类时,应当将界面的功能由COM组件转到包装类中,否则,可以在COM对象中实现界面,并被代理到可管理的包装类中。

通过重新安排代码,我们能够使互操作层的代价最小,并拥有一个从可管理代码到对象的最简单自然的界面。编写自定义的可管理包装类的带来的另一个好处是可以移动远程对象的分界线。

类的界面的实现

在可管理代码中,类的界面的定义并不是显性的,该界面包含适用于.NET对象使用的所有公共方法、属性、域和事件。它可以是一个双重或者仅起调度作用的界面,类界面的名字为.NET类名字前加一下划线。例如,对于Mammals而言,类界面的名字是_Mammals。对于派生类而言,类界面也需要实现基本类所有的公共方法、域和属性,派生类还实现每个基本类的界面。例如,Mammals类扩展了类MammalMainclass,.NET对象向COM客户端提供三个名字为_Mammals、_MammalMainclass和_Object的界面。

COM客户端能够获得名字为Mammals的类界面,该界面定义在由类型库输出向导(Tlbexp.exe)工具生成的类型库中。如果Mammals类实现一个或多个界面,这些界面将出现在coclass中。

[odl, uuid(0000...0000), hidden, dual, nonextensible, oleautomation]

interface _Mammals : IDispatch

{

[id(0x00000000), propget] HRESULT ToString([out, retval] BSTR*

pRetVal);

[id(0x60020001)] HRESULT Equals([in] V ARIANT obj, [out, retval]

V ARIANT_BOOL* pRetVal);

[id(0x60020002)] HRESULT GetHashCodes([out, retval] short* pRetVal);

[id(0x60020003)] HRESULT GetType([out, retval] _Type** pRetVal);

[id(0x6002000d)] HRESULT EatIt();

[id(0x6002000e)] HRESULT GetBreathe();

[id(0x6002000f)] HRESULT GoSleep();

}

[uuid(0000...0000)]

coclass Mammals

{

[default] interface _Mammals;

}

类界面生成是可选的。如果没有选择其他选项,COM互操作层为每个输出到类库中的类生成一个只起调度作用的界面,通过在类中添加ClassInterfaceAttribute属性,我们可以中止或修改这一界面的自动创建。尽管类界面能够使我们无须向COM提供可管理类,但其用户还是受到了一定的限制。

主互操作组合体的使用

与其他的.NET组合体不同,互操作组合体不包括有任何实现代码,它只包括有已经在COM组件中实现的类的类型定义。正是从这些类型定义中,CLR生成了RCW,使可管理代码能够在编译时与类型进行绑定,并向CLR提供类型在运行时应当如何进行组织的信息。一个COM类可以生成任意数量的组合体(使用tlbimp是生成互操作组合体的一种方法),但只有一个组合体被称作是主互操作组合体(PIA),PIA中包含有软件开发商对类型的描述,是由开发商签名和授权使用的。由于PIA是由软件开发商签名,并且包含有PrimaryInteropAssembly属性,因此,我们非常容易把它与定义同一个COM类型的其他互操作层区别开来。需要引起注意的是,工程系统会检查指定类库的主互操作组合体是否存在,如果存在,主互操作组合体将被用作COM对象的方法和属性的包装类;如果不存在,则其处理方式将与tlbimp工具指定的相同。

由于可以唯一地标识一个类,主互操作组合体是非常重要的。在由非组件开发商提供的互操作组合体中定义的类型与主互操作组合体中定义的类型是兼容的。例如,考虑如下的情况,如果一个公司中的二名开发人员正在编写与第三方提供的COM组件实现互操作的可管理代码,其中的一个开发人员获得了组件开发商提供的PIA,另一个开发人员则使用tlbimp 依据COM对象的类库生成了自己的互操作组合体。只要其中的一位开发人员(甚至是第三方开发人员或客户)不试图向另一个开发人员的代码中传递对象,每个开发人员的代码就没有什么问题。如果出现这种情况,就会出现类型匹配异常。尽管它们表示的是同一个COM 对象,但CLR的类型检查功能认为这二个组合体包含着不同的类型。

我们应当获得在应用程序中使用的任意COM组件的PIA,这样有助于阻止由使用相同COM对象在代码编写中出现不兼容的情况。我们也应当提供自己开发、但可能被别人使用的任何组件的PIA,尤其是第三方开发人员或客户要在他们的应用程序中使用这些组件时更是这样。

相关主题
相关文档
最新文档