438 ASP3高级编程 China pul coM 下载 TElevision接口,那么一个遥控器就能控制所有的电视机!不管电视机是哪个厂家生产的,只 要提供的控制接口相同,遥控器就能控制它。 1421组件 上面例子中的遥控器就是一个COM组件,在VB中编译一个包含在一个 ActiveX项目中的 类模块时,就创建了一个组件,如果这个项目含有多个类模块,那么就创建了多个组件 组件就是通过实现一个或多个接口来提供功能的某种东西 简单地说,组件就是一个具有唯一名称的功能体,并以某种形式的DLL或EXE封装或分 布。在VB中编译一个含有四个类模块的 Activex对象时,所创建的就是含有四个COM组件的 COM服务器,每个组件对应一个类模块,生成了四个COM接口,每个接口对应一个组件,这 些接口(类模块)通常提供了能够访问的方法和属性。类模块与COM组件的关系如图14-3所示 VB Activex项目 COM服务器 类模块 COM组件 COM组件 类模块 类模块 COM组件 COM组件 图14-3类模块与COM组件的关系 棒形图( Lollypop Diagram) COM中运用了一种简单的图解方法来表现组件支 持的接口,即棒形图。这些棒形图中用一个方框表示 REmote Control 组件,方框中的名称就是组件名称,方框左侧伸出的 o大遥控器 部分表示组件的接口,方框上方伸出的单线表示一个 称为 IUnknown的接口,这是每个组件必须实现的,下 I control 面很快就要讲述这个重要的接口。 小遥控器 遥控器的棒形图如图14-4所示 这个图显示了两个组件(电视机),通过 REmote Control接口提供简单的频道变换能力,换句话说就是 14-4遥控器棒形图 遥控。这个图并不复杂,但清楚地表示了可以用它来控制电视机 IUnknoun接口是唯一从方框上面出去的接口,所以在图上未标注它。 1422缺省接口 创建COM组件时,这个组件可以包含很多接口,COM允许把其中一个接口设置为客户使 用的缺省接口,不能自己选择指定接口的客户将使用缺省接口,其理由下面讲述。 在VB中创建一个类模块,并编译成一个组件时,并不能控制哪个接口成为缺省接口, 个缺省接口通常不仅包括所定义的类模块,还包括公共的方法和属性。可以通过使用
I Te l e v i s i o n接口,那么一个遥控器就能控制所有的电视机!不管电视机是哪个厂家生产的,只 要提供的控制接口相同,遥控器就能控制它。 14.2.1 组件 上面例子中的遥控器就是一个 C O M组件,在V B中编译一个包含在一个 A c t i v e X项目中的 类模块时,就创建了一个组件,如果这个项目含有多个类模块,那么就创建了多个组件。 组件就是通过实现一个或多个接口来提供功能的某种东西。 简单地说,组件就是一个具有唯一名称的功能体,并以某种形式的 D L L或E X E封装或分 布。在V B中编译一个含有四个类模块的 A c t i v e x对象时,所创建的就是含有四个 C O M组件的 C O M服务器,每个组件对应一个类模块,生成了四个 C O M接口,每个接口对应一个组件,这 些接口(类模块)通常提供了能够访问的方法和属性。类模块与 C O M组件的关系如图1 4 - 3所示。 图14-3 类模块与C O M组件的关系 棒形图(Lollypop Diagram) C O M中运用了一种简单的图解方法来表现组件支 持的接口,即棒形图。这些棒形图中用一个方框表示 组件,方框中的名称就是组件名称,方框左侧伸出的 部分表示组件的接口,方框上方伸出的单线表示一个 称为I U n k n o w n的接口,这是每个组件必须实现的,下 面很快就要讲述这个重要的接口。 遥控器的棒形图如图1 4 - 4所示。 这个图显示了两个组件 (电视机 ),通过 I R e m o t e C o n t r o l接口提供简单的频道变换能力,换句话说就是 遥控。这个图并不复杂,但清楚地表示了可以用它来控制电视机。 I U n k n o u n接口是唯一从方框上面出去的接口,所以在图上未标注它。 14.2.2 缺省接口 创建C O M组件时,这个组件可以包含很多接口, C O M允许把其中一个接口设置为客户使 用的缺省接口,不能自己选择指定接口的客户将使用缺省接口,其理由下面讲述。 在V B中创建一个类模块,并编译成一个组件时,并不能控制哪个接口成为缺省接口,一 个缺省接口通常不仅包括所定义的类模块,还包括公共的方法和属性。可以通过使用 438计计ASP 3 高级编程 下载 VB ActiveX项目 类模块 类模块 编译 COM组件 COM组件 COM组件 COM组件 COM服务器 类模块 类模块 图14-4 遥控器棒形图 IRemoteControl 大遥控器 小遥控器 IRemoteControl
chinapub coM BIu& COM. COM. RASP 439 下载 Implements关键字使得一个类模块支持附加的接口,但缺省时ASP不能调用这些接口的方法 在 ActiveX项目中创建一个公共类模块时,B所创建的接口的名称就是类模块的 名称前加一个短下划线,例如,MyTv类模块的缺省接口就是_MyTV 1423GUID—实体的确定名称 编译一个类模块并创建一个COM组件时,VB就为这件组件赋予了一个全局唯一的标识符, 称为GUID,GUID是一个根据系统时钟和网卡的MAC地址生成的一个128位的数字,保证是 如果没有网卡,GUID仍然是唯一的,但只能保证在本地计算机中是唯一的 GUID是一些标识符的专用术语,这些标识符跨时间和空间唯一准确地表示一个实体,由 于GUID采用128位数,按每秒增加一千万个GUID的速度,可以使用到公元5770年 当GIUD用来标识一个类模块时,被称为类标识符( CLSID)。在COM中,可以使用GUID 来标识很多东西,所以,在不同的环境中GUID有不同的名称,这些环境包括接口标识符(ID) 和应用程序标识符( APPID) GUID的用途 为什么需要用一个128位数字来标识一个组件?难道是我们给类模块取的名称不够用吗? 答案当然否定的。全球所有的分析家和开发人员每时每刻都在给他们的类模块命名(逻辑名称), 所以两个人同时使用同一种逻辑名称的情况会经常出现,尤其是当人们为同一个范围的问题 设计应用程序时。解决这个问题的办法就是在名字中增加一部分标识符,这并不困难 128位数字是毫无含义的,也不好使用。人们在注册和其他必须处理GUID的地方用一个 字符串来代替GUID。以下的 CLSID用来标识(物理名称)用于微软 ADO Connection组件的一个 组件: 00000293-0000-0010-8000-00AA006D2EA4 这不是一个程序化的标识符( ProgID),后面将讨论PogD 1424接口的详细内容 正如前面提到的,COM是一种二进制规范,这个二进制规范包括描述一个接口在存储器 中的形式以及如何在运行期中访问 虚拟方法表 定义一个接口时,接口方法的次序、每一种方法的参数和各种其他的属性,例如接口的 GUID等,都要通过接口特征来定义。当编译一个包含COM组件的DLL或EXE文件时,接口 特征(二进制形式)就被转换进创建的文件,这个信息用来建立虚拟方法表( vtable),它决定 个客户在运行期如何调用接口的各种方法。为了便于理解,可以把一个 vtable看作一个含有各 种函数的N维数组,这里的N表示一个接口所含方法的个数,如图14-5所示 这个数组实际上并不含有代码,但含有能定位代码地址的指针。因此,通过使用 vtable 可知下标0的项目指向 TurnOn’方法,下标1的项目指向 Change Channel方法,等等。 当编译使用一个接口的客户代码时,这些数组下标(例如0,1,…)就放置在所创建的DLL 戊EXE中,这就是早期绑定。客户知道如何通过按给定的偏移量访问某个函数,从而调用接
I m p l e m e n t s关键字使得一个类模块支持附加的接口,但缺省时 A S P不能调用这些接口的方法。 在A c t i v e X项目中创建一个公共类模块时, V B所创建的接口的名称就是类模块的 名称前加一个短下划线,例如, M y T V类模块的缺省接口就是_ M y T V。 14.2.3 GUID—实体的确定名称 编译一个类模块并创建一个 C O M组件时,V B就为这件组件赋予了一个全局唯一的标识符, 称为G U I D,G U I D是一个根据系统时钟和网卡的 M A C地址生成的一个 1 2 8位的数字,保证是 唯一的。 如果没有网卡,G U I D仍然是唯一的,但只能保证在本地计算机中是唯一的。 G U I D是一些标识符的专用术语,这些标识符跨时间和空间唯一准确地表示一个实体,由 于G U I D采用1 2 8位数,按每秒增加一千万个 G U I D的速度,可以使用到公元5 7 7 0年。 当G I U D用来标识一个类模块时,被称为类标识符 ( C L S I D )。在C O M中,可以使用G U I D 来标识很多东西,所以,在不同的环境中 G U I D有不同的名称,这些环境包括接口标识符 ( I I D ) 和应用程序标识符( A P P I D )。 G U I D的用途 为什么需要用一个1 2 8位数字来标识一个组件?难道是我们给类模块取的名称不够用吗? 答案当然否定的。全球所有的分析家和开发人员每时每刻都在给他们的类模块命名 (逻辑名称), 所以两个人同时使用同一种逻辑名称的情况会经常出现,尤其是当人们为同一个范围的问题 设计应用程序时。解决这个问题的办法就是在名字中增加一部分标识符,这并不困难。 1 2 8位数字是毫无含义的,也不好使用。人们在注册和其他必须处理 G U I D的地方用一个 字符串来代替G U I D。以下的C L S I D用来标识(物理名称)用于微软ADO Co n n e c t i o n组件的一个 组件: 这不是一个程序化的标识符 ( P r o g I D ),后面将讨论P r o g I D。 14.2.4 接口的详细内容 正如前面提到的, C O M是一种二进制规范,这个二进制规范包括描述一个接口在存储器 中的形式以及如何在运行期中访问。 1. 虚拟方法表 定义一个接口时,接口方法的次序、每一种方法的参数和各种其他的属性,例如接口的 G U I D等,都要通过接口特征来定义。当编译一个包含 C O M组件的D L L或E X E文件时,接口 特征(二进制形式)就被转换进创建的文件,这个信息用来建立虚拟方法表 ( v t a b l e ),它决定一 个客户在运行期如何调用接口的各种方法。为了便于理解,可以把一个 v t a b l e看作一个含有各 种函数的N维数组,这里的N表示一个接口所含方法的个数,如图 1 4 - 5所示。 这个数组实际上并不含有代码,但含有能定位代码地址的指针。因此,通过使用 v t a b l e, 可知下标0的项目指向Tu r n O n O ff方法,下标1的项目指向C h a n g e C h a n n e l方法,等等。 当编译使用一个接口的客户代码时,这些数组下标 (例如0,1,. . . )就放置在所创建的D L L 或E X E中,这就是早期绑定。客户知道如何通过按给定的偏移量访问某个函数,从而调用接 第1 4章 C O M、C O M +和A S P计计439 下载
440Ap;高箱程 Chinapub.coM 下载 口中的一个方法。客户不必在运行期查询任何附加信息,只要有接口指针就能进行调用。接 口指针是一种指向可以调用的函数的数组的指针 Remote control Public Sub TurnOnoffo Change channe ot On/off Increase volume Public Sub Change Channel(Number) Decreasevolume End Sub Getchannel 图14-5虚拟方法表 属性就是函数 提供读写对象的数据(或状态)的能力的接口方法叫做属性。下面讲述的方法与C++中的方 法类似,但与ⅤB中给出的例子不同。从语义的角度看它们是相同的,VB也是一种很好的工 具:但在实现时却不是,VB引入的封裝层可能导致人们的误解。 只读属性等同于单个接口方法,该方法允许读取一个值。 ·只写属性等同于单个接口方法,该方法允许更新一个值。 读/写属性等同于两个接口方法,两个方法分别允许读取和更新一个值。 因此,如果有四个读/写属性,VB就会创建8个方法来读取和更新这四个值。 2.接口的要素 一般来说,接口通常至少有一个方法,最多1024个(COM和跨场所调度的限制导致的限 制),一个接口有多少方法是一个设计问题,这个问题是由程序员决定的,可以有一个或者多 个,但不是必须有,一个接口可以没有方法。通常一个设计得很好的接口的方法不超过10到 个 没有方法的接口是不常见的,但也是有用的。它常用于提供组件和顾客间的一种秘密交 流或信号,就像你约好了一没见过面的人,约定他穿着一件特别的衣服,因而当你在人多的 场合遇见他时,能很快识别他。客户通过接口能检查确保组件是存在的 这里的要素化指的是逻辑上把相关的方法一起放到一个接口,因此,如果我说 小心接口要素化,意思是你应当特别注意那些放在接口的方法 3.接口的原则 从许多方面来看,接口设计与用户界面设计相似。对于用户界面设计问题,需要考虑用 户想通过界面做什么,并且使用户非常简单地知道如何做他们想做的事,并且能通过界面去 做,而最后一步(做)是最重要的 不同之处在于,在进行用户界面设计时我们处理的是控件,像文本框和单选按钮,及它 们在一个或多个窗体上的布局。对于组件设计,我们处理的是属性和方法,以及它们对一个 或多个接口的影响,影响的接口越多,对客户就越有用。 就像用户界面设计,COM接口设计从某种意义上讲是一个基于经验的过程。也许为某 个项目采用一种方式设计,因为它适合这些客户:也许因为有特殊限制或技术上的可能而采 用另一种方式设计:不管采用哪种方式,其目的就是让客户满意
口中的一个方法。客户不必在运行期查询任何附加信息,只要有接口指针就能进行调用。接 口指针是一种指向可以调用的函数的数组的指针。 图14-5 虚拟方法表 属性就是函数 提供读写对象的数据 (或状态)的能力的接口方法叫做属性。下面讲述的方法与 C + +中的方 法类似,但与 V B中给出的例子不同。从语义的角度看它们是相同的, V B也是一种很好的工 具;但在实现时却不是,V B引入的封装层可能导致人们的误解。 • 只读属性等同于单个接口方法,该方法允许读取一个值。 • 只写属性等同于单个接口方法,该方法允许更新一个值。 • 读/写属性等同于两个接口方法,两个方法分别允许读取和更新一个值。 因此,如果有四个读/写属性, V B就会创建8个方法来读取和更新这四个值。 2. 接口的要素 一般来说,接口通常至少有一个方法,最多 1 0 2 4个( C O M和跨场所调度的限制导致的限 制),一个接口有多少方法是一个设计问题,这个问题是由程序员决定的,可以有一个或者多 个,但不是必须有,一个接口可以没有方法。通常一个设计得很好的接口的方法不超过 1 0到 1 5个。 没有方法的接口是不常见的,但也是有用的。它常用于提供组件和顾客间的一种秘密交 流或信号,就像你约好了一没见过面的人,约定他穿着一件特别的衣服,因而当你在人多的 场合遇见他时,能很快识别他。客户通过接口能检查确保组件是存在的。 这里的要素化指的是逻辑上把相关的方法一起放到一个接口,因此,如果我说 小心接口要素化,意思是你应当特别注意那些放在接口的方法。 3. 接口的原则 从许多方面来看,接口设计与用户界面设计相似。对于用户界面设计问题,需要考虑用 户想通过界面做什么,并且使用户非常简单地知道如何做他们想做的事,并且能通过界面去 做,而最后一步(做)是最重要的。 不同之处在于,在进行用户界面设计时我们处理的是控件,像文本框和单选按钮,及它 们在一个或多个窗体上的布局。对于组件设计,我们处理的是属性和方法,以及它们对一个 或多个接口的影响,影响的接口越多,对客户就越有用。 就像用户界面设计, C O M接口设计从某种意义上讲是一个基于经验的过程。也许为某一 个项目采用一种方式设计,因为它适合这些客户;也许因为有特殊限制或技术上的可能而采 用另一种方式设计;不管采用哪种方式,其目的就是让客户满意。 440计计ASP 3 高级编程 下载 组 件 客户
hinaopub.com 第4章cCOM.CoM+和sp441 下载 本书不可能对所遇到的每个问题都给出一套实际的接口设计大纲,但设计得好的接口有 一些共同特征: ·确保接口对使用者有用。COM有定义接口的能力,这些接口在语言上或者应用程序上 是不友好的,也就是说它们不能运行。当把COM设计成一种二进制标准时,一些语言 就比另一些语言功能更强,就像一些浏览器比其他浏览器功能更多一样。如果COM限 制这些语言,就不能得到广泛地使用,因此,必须认真考虑接口的性能。例如,一个主 要用于像ASP这样的脚本语言环境的组件,应当使用 Variant作为参数,并且参数能够输 入、修改和输出。这是脚本引擎的一个特征。因此,必须确保接口遵守这些原则并能在 这样的环境中正常工作 ·接口名称(或类模块)和方法名称采用描述性的名称。调用一个叫 IDoSomething接口和使 用一个叫Dolt的方法没什么意义,这些名称应该有意义,应尽可能说明它在客户的问题 中所完成的功能。 ·方法应当很好地要素化,逻辑相关,并不要有太多的方法。如果你已经有一个 IChannelselector的接口,它只应当含有与频道转换有关的方法。例如,如果需要 Fine Tune channel的方法,它应该包含在 I ChannelTuner接口中 属性相同。接口的属性应当与客户感兴趣的信息类型一致。 ·接口应当是强类型的。像VB和C++这些语言都是强类型的,也就是说我们所定义的变 量属于某一种类型,它只会有这种类型的数据,例如一个数(Long型或 Integer型),如果 有人想分配给该变量一个 String型数据,这时,编译器就会产生错误。ASP只支持像 Ⅴ SCript这样的弱类型脚本语言,在 SCript中,所有的变量都被定义为 Variant型,这 种类型的变量可以含有任何类型的数据。把接口定义为强类型的好处是使用时简单明了, 可以看到所使用的参数的类型,而不必猜测一个方法能处理什么数据类型。 ·喜欢使用自己的接口。这是一个全球所有成功的公司所遵循的金科玉律:使用自己的接 口,或者至少应该确信所使用的接口是个好接口。 4.接口的不变性 旦设计好一个COM接口,并且通过某些形式向客户发布了,这个接口就应当被认为是 不可变的,不能对其做任何可能影响其二进制表现的改动。 看一下 I Volume Control接口的描述,其中有 Increase Volume方法和 Decrease volume方法 如果发布了这个组件,而且人们把这个遥控组件用在他们的应用程序中,并使用了我们的接 口及其所有方法。如果我们再决定去掉 Decrease volume方法会怎么样呢?显然,那些应用程 序将被严重破坏,或者将会有一些非常烦人的相关问题,如果改变一个方法参数的数量或者 类型,也可能会产生同样的问题 因此,有一些原则需要遵守 在对一个接口满意前,不要发表他。 如果实在需要改变一个接口,不应修改已存在的这个接口,而是为所需的扩展功能再创 建一个接口。这当然要求客户类型支持多重接口,但ASP目前不支持多重接口 个接口一旦发布,就不能改变其中的任何一部分,这包括方法的顺序、参数类型等。 如果改变了一个已经存在的接口(我们推荐不要这样),应重新编译所有使用该接口的客 户应用程序。就像我们已经讲过的,使用早期绑定的客户将会把接口的设计硬编码为
本书不可能对所遇到的每个问题都给出一套实际的接口设计大纲,但设计得好的接口有 一些共同特征: • 确保接口对使用者有用。 C O M有定义接口的能力,这些接口在语言上或者应用程序上 是不友好的,也就是说它们不能运行。当把 C O M设计成一种二进制标准时,一些语言 就比另一些语言功能更强,就像一些浏览器比其他浏览器功能更多一样。如果 C O M限 制这些语言,就不能得到广泛地使用,因此,必须认真考虑接口的性能。例如,一个主 要用于像A S P这样的脚本语言环境的组件,应当使用 Va r i a n t作为参数,并且参数能够输 入、修改和输出。这是脚本引擎的一个特征。因此,必须确保接口遵守这些原则并能在 这样的环境中正常工作。 • 接口名称(或类模块)和方法名称采用描述性的名称。调用一个叫 I D o S o m e t h i n g接口和使 用一个叫D o I t的方法没什么意义,这些名称应该有意义,应尽可能说明它在客户的问题 中所完成的功能。 • 方法应当很好地要素化,逻辑相关,并不要有太多的方法。如果你已经有一个 ICh a n n e l S e l e c t o r的接口,它只应当含有与频道转换有关的方法。例如,如果需要一个 F i n e Tu n e C h a n n e l的方法,它应该包含在I C h a n n e l Tu n e r接口中。 • 属性相同。接口的属性应当与客户感兴趣的信息类型一致。 • 接口应当是强类型的。像 V B和C + +这些语言都是强类型的,也就是说我们所定义的变 量属于某一种类型,它只会有这种类型的数据,例如一个数 ( L o n g型或I n t e g e r型),如果 有人想分配给该变量一个 S t r i n g型数据,这时,编译器就会产生错误。 A S P只支持像 V B S c r i p t这样的弱类型脚本语言,在 V B S c r i p t中,所有的变量都被定义为 Va r i a n t型,这 种类型的变量可以含有任何类型的数据。把接口定义为强类型的好处是使用时简单明了, 可以看到所使用的参数的类型,而不必猜测一个方法能处理什么数据类型。 • 喜欢使用自己的接口。这是一个全球所有成功的公司所遵循的金科玉律:使用自己的接 口,或者至少应该确信所使用的接口是个好接口。 4. 接口的不变性 一旦设计好一个 C O M接口,并且通过某些形式向客户发布了,这个接口就应当被认为是 不可变的,不能对其做任何可能影响其二进制表现的改动。 看一下I Vo l u m e C o n t r o l接口的描述,其中有 I n c r e a s e Vo l u m e方法和D e c r e a s e Vo l u m e方法。 如果发布了这个组件,而且人们把这个遥控组件用在他们的应用程序中,并使用了我们的接 口及其所有方法。如果我们再决定去掉 D e c r e a s e Vo l u m e方法会怎么样呢?显然,那些应用程 序将被严重破坏,或者将会有一些非常烦人的相关问题,如果改变一个方法参数的数量或者 类型,也可能会产生同样的问题。 因此,有一些原则需要遵守: • 在对一个接口满意前,不要发表他。 • 如果实在需要改变一个接口,不应修改已存在的这个接口,而是为所需的扩展功能再创 建一个接口。这当然要求客户类型支持多重接口,但 A S P目前不支持多重接口。 • 一个接口一旦发布,就不能改变其中的任何一部分,这包括方法的顺序、参数类型等。 如果改变了一个已经存在的接口 (我们推荐不要这样 ),应重新编译所有使用该接口的客 户应用程序。就像我们已经讲过的,使用早期绑定的客户将会把接口的设计硬编码为 第1 4章 C O M、C O M +和A S P计计441 下载