📄 205001.htm
字号:
<html><body><span id=Layer1><p><font size=2 color=#3c3c3c face=arial>对於Microsoft Windows 2000,没有任何软体开发技术比元件物件模型还要更基本的了。几乎每个在这个平台上或Microsoft其它作业系统上撰写的应用程式都会使用COM。即使跨多个系统执行的应用程式也能使用增强型式的COM,就是所谓的分散式的COM(Distributed COM,DCOM)。</span><span id=Layer2></font></p><p><font size=2 color=#3c3c3c face=arial>为Windows 2000撰写软体表示使用COM</span><span id=Layer3></font></p><hr><font face=Arial Black color=#3e77d7 size=3><b></b></font><p><font size=2 color=#3c3c3c face=arial>为Windows 2000撰写软体表示使用COM</span><span id=Layer4></font></p><hr><p><font size=2 color=#3c3c3c face=arial>Windows 2000引进了COM+,它是这个核心技术的延伸版本。本章将详细地介绍COM的基本,大部份的技术和COM+是一样没有改变的。若要详细了解COM+的特性,可参考</span><span id=Layer5> <a target='_new' href=208.htm#>第八章</span><span id=Layer6></a> 。</span><span id=Layer7></font></p><a name=205001><font color=#3e70d7 face=arial size=5><b>了解COM物件</span><span id=Layer8></b></font><p><font size=2 color=#3c3c3c face=arial>在COM中,客户端存取COM物件所提供的服务。第一个该回答的问题便是:什麽是一个COM物件?就像其它的类型的物件一样,一个COM物件就是一个抽象的软体,封装两个东西:资料与method。一个物件的资料,也称为它的状态,就是指物件储存在记忆体中的资讯,而它的method就是它的程式码,允许物件提供它的服务。举例来说,一个COM物件允许销售员开出的订单内包含订单明细资料,这些资料是订单的一部份,以及订单建立的时间。这个物件的method包含将订单项目加到订单、提交订单、判断订单建立的时间...等等方法。</span><span id=Layer9></font></p><p><font size=2 color=#3c3c3c face=arial>一个COM物件有两样东西:资料和method</span><span id=Layer10></font></p><hr><font face=Arial Black color=#3e77d7 size=3><b></b></font><p><font size=2 color=#3c3c3c face=arial>一个COM物件有两样东西:资料和method</span><span id=Layer11></font></p><hr><p><font size=2 color=#3c3c3c face=arial>就类似在程式语言中的method一样,一个COM物件的method可以拥有参数。举例来说,一个新增一个项目到订单的method可能夹带一个整数参数,其中包含欲新增订单项目的数量。在COM中,method必须组成介面,而每个COM物件都要实作一些介面。(实际上数目上总是超过一个,理由稍後再解释。)举例来说,刚提及的订单物件可能会将所有建立一份订单用到的所有method组成一个介面,所有用来处理与订单相关资讯的method,如建立订单,可能会组成第二个介面。介面只显露一个物件的method,资料已封装,只能透过物件实作的介面中的method存取。如</span><span id=Layer12> <a target='_new' href=201.htm#>第一章</span><span id=Layer13></a> 所展示的一样,物件提供的每一个介面在惯例上是使用一个小圈圈连结到一个物件来表示。</span><span id=Layer14></font></p><p><font size=2 color=#3c3c3c face=arial>许多Method组成许多介面</span><span id=Layer15></font></p><hr><font face=Arial Black color=#3e77d7 size=3><b></b></font><p><font size=2 color=#3c3c3c face=arial>许多Method组成许多介面</span><span id=Layer16></font></p><hr><p><font size=2 color=#3c3c3c face=arial>每个COM物件都有一个或多个客户端使用它的服务之软体。若要存取一个物件的服务,客户端必须呼叫物件所提供的method。在呼叫特定介面上的method之前,客户端必须要求指向那个介面的介面指标。一切端赖客户端所使用的程式开发语言的不同,客户端不一定可以看到真正的指标;不过不管它看起来像什麽样子,它允许客户端呼叫它所参考到的介面内的method。就如图5-1中显示,客户端握住同一个COM物件的多个介面指标是很平常的事,每一个介面一个指标,指向它欲呼叫的介面。</span><span id=Layer17></font></p><p><font size=2 color=#3c3c3c face=arial>客户端使用介面指标呼叫method</span><span id=Layer18></font></p><hr><font face=Arial Black color=#3e77d7 size=3><b></b></font><p><font size=2 color=#3c3c3c face=arial>客户端使用介面指标呼叫method</span><span id=Layer19></font></p><hr><br><center><a target=_new href=imagesh/5-1.gif><img border=0 src='imagesl/5-1.gif'></a></center></span><span id=Layer20><center><table border=0 ><td align=center><font color=#3c3c3c face=arial size=2><font size=2 face=arial color=#3e80d7><b> 图5-1</span><span id=Layer21> </b></font>若要呼叫特定介面上的method,客户端必须握住指向那个介面的介面指标。</span><span id=Layer22></td></table></font></center><p><font size=2 color=#3c3c3c face=arial>在更深入探讨介面之前,得要强调一下COM物件可以使用许多不同的程式开发语言建立。事实上,一个COM物件与它的客户端可能,或者也可能不是以同一种程式语言写成的。因为COM定义了与语言独立的一种标准,以供客户端和物件进行互动。COM程式设计师最普遍的选择便是Microsoft Visual Basic、C++与Java,但现在几乎所有在Windows 2000上与Microsoft其它作业系统的开发工具都能够支援COM。这叁种最流行的语言每一种都对物件有自己的解释。这些概念几乎是很类似的,也和COM对物件的定义相近,但是它们并不是完全相同。撰写使用COM的软体需要了解COM对物件的观点:它们都是相同的,不管使用的程式语言是哪一种,然後看看这些观点要如何对应到特定的程式语言。</span><span id=Layer23></font></p><p><font size=2 color=#3c3c3c face=arial>COM物件可让许多不同的程式开发语言使用</span><span id=Layer24></font></p><hr><font face=Arial Black color=#3e77d7 size=3><b></b></font><p><font size=2 color=#3c3c3c face=arial>COM物件可让许多不同的程式开发语言使用</span><span id=Layer25></font></p><hr><p><font size=2 color=#3c3c3c face=arial>不用太讶异,C++的程式设计师要了解的最详细,如果选择使用COM的话,就得很痛苦地了解COM的低阶部份。他们也可以使用高阶的开发工具,如现在流行的Microsoft ActiveX Template Library (ATL) 来隐藏许多的细节。对照之下,对Visual Basic的程式设计师而言,COM的细节已完全隐藏起来了。因此在Visual Basic中使用COM就变得很简单,但有时也令人很 气。Visual Basic/COM的对照不见得都相同。最後,很意外地,Java对物件原生的观点和COM非常类似。Microsoft的Java virtual machine (JVM)的伟大功勋便是支援Java物件到COM物件直接的对应,反之亦然。若使用Microsoft JVM,Java可能是COM程式设计师最自然的语言,为的就是这两类物件最简单直觉的对应关系了。</span><span id=Layer26></font></p><font color=#3e72d7 face=arial size=4><b>介面(Interface)</span><span id=Layer27></b></font><p><font size=2 color=#3c3c3c face=arial>在COM中,整体看来一个客户端从来就不会握住一个正在执行的物件之参考。而是客户端尽透过呼叫介面指标来呼叫物件的method与其互动。从客户端的观点,一个COM物件主要可视为一群介面的集合。不管用来撰写物件的语言为何,COM介面永远符合一个定义在记忆体中的结构。为了让客户端呼叫物件上的method,透过正确的记忆体布局,COM让使用某一种语言撰写的COM物件和其它语言写的物件相互沟通。</span><span id=Layer28></font></p><p><font size=2 color=#3c3c3c face=arial>COM定义介面在记忆体中的结构</span><span id=Layer29></font></p><hr><font face=Arial Black color=#3e77d7 size=3><b></b></font><p><font size=2 color=#3c3c3c face=arial>COM定义介面在记忆体中的结构</span><span id=Layer30></font></p><hr><p><font size=2 color=#3c3c3c face=arial><font size=2 face=arial color=#3e80d7><b> 介面的类型</span><span id=Layer31> </b></font>在一个完美的世界,一个介面的样式可以用在所有客户端与物件的组合。然而在不完美的世界里,COM的存在就不是那麽回事了,因此便存在着叁种不同类型的COM介面。在COM的演进过程中,每一种介面都是为了特殊的理由存在的,如果COM是在今天建立的,那麽这些差异点可能就不存在了。</span><span id=Layer32></font></p><p><font size=2 color=#3c3c3c face=arial>COM拥有叁种类型的介面</span><span id=Layer33></font></p><hr><font face=Arial Black color=#3e77d7 size=3><b></b></font><p><font size=2 color=#3c3c3c face=arial>COM拥有叁种类型的介面</span><span id=Layer34></font></p><hr><p><font size=2 color=#3c3c3c face=arial>第一个选择,vtable介面,最原始是为C++设计的。也就是所谓的自订介面,它们记忆体中的结构映照到流行的C++编译器中method呼叫的方式(包含Microsoft Visual C++)。如此在C++中实作vtable介面的动作就变得相当简单,并能很有效地透过vtable介面呼叫C++撰写的物件之method。</span><span id=Layer35></font></p><p><font size=2 color=#3c3c3c face=arial>第一种介面类型,vtable介面,最初是为C++定义的</span><span id=Layer36></font></p><hr><font face=Arial Black color=#3e77d7 size=3><b></b></font><p><font size=2 color=#3c3c3c face=arial>第一种介面类型,vtable介面,最初是为C++定义的</span><span id=Layer37></font></p><hr><p><font size=2 color=#3c3c3c face=arial>今天,vtable介面也能够很简单、快速,并普遍地在Visual Basic与Java中使用。不过当COM在1993年初次发表时,vtable介面并不适合Visual Basic使用。还有一点,Visual Basic程式在呼叫使用C++导向的介面结构之method时仍有一些挑战。不过对Visual Basic来说这不是vtable介面带来的唯一问题。对任何技术来说,还有一个更棘手的问题,便是如何让以不同程式语言撰写的客户端与物件互动:如何转换某种语言的资料型态到另一种语言定义的资料型态呢?假设一个使用C++撰写的物件将要给Visual Basic客户端使用,那麽若这个物件介面中其中一个method实作时,以一个指标当做参数,这会发生什麽事? Visual Basic并不支援指标,因此要从Visual Basic客户端呼叫这个method便成为一件不可能的事。当两个语言牵涉到两者都支援一种特殊的资料型态,但却以不同的方式来处理时,一个更微妙但无伤大雅的问题就发生了。举例来说,C++与Visual Basic两者都支援字元字串,不过它们处理字串的方式却有天壤之别。这些问题该怎麽解决呢?</span><span id=Layer38></font></p><p><font size=2 color=#3c3c3c face=arial>COM一开始的答案便是定义第二类型的介面,通常称为分派介面(dispatch interface),但更常被称为dispinterface。dispinterface可让Visual Basic程式能够有效地实作COM物件,并能扮演COM物件的客户端。Dispinterface在记忆体中的结构与vtable的结构有一些不同,以便让Visual Basic更容易呼叫method。(事实上,每个dispinterface依赖於一个特定的vtable介面,称IDispatch。)同样地,在dispinterface中,method的参数只限定於某些资料型态。和vtable介面不一样的地方在於,vtable介面的method能接受的参数是限定在C++中定义的型态;dispinterface的资料型态基本上只限制於Visual Basic中可用的(虽然dispinterface同样也可以在Java中使用)。因为使用dispinterface有时也称做自动化(automation),所以一个介面的参数型态如果遵循这个限制就可称为自动化相容(automation-compatible)。</span><span id=Layer39></font></p><p><font size=2 color=#3c3c3c face=arial>第二个介面类型,dispin-terface,一开始是为了Visual Basic而定义的</span><span id=Layer40></font></p><hr><font face=Arial Black color=#3e77d7 size=3><b></b></font><p><font size=2 color=#3c3c3c face=arial>第二个介面类型,dispin-terface,一开始是为了Visual Basic而定义的</span><span id=Layer41></font></p><hr><p><font size=2 color=#3c3c3c face=arial>当参数传送到一个dispinterface method,或从这个method传出时,这些参数都是包装成一个或多个Variant。一个Variant包含任何自动化相容型态的值,同样的也包含一个指示,指明它的资料型态。举例来说,一个特定的Variant可能包含某些整数值,以及一个标注(Tag)指明它的资料是一个长整数。Variant对於Visual Basic程式设计师来说是很方便的,Visual Basic会为你进行所有将参数转换成这个格式的动作,反之亦然。</span><span id=Layer42></font></p><p><font size=2 color=#3c3c3c face=arial>今天,Visual Basic又提升了,它现在可以让Visual Basic的客户端使用vtable,以及这些自动化相容的介面来存取或实作COM物件。而第叁种可用的介面是,Dual Interface。一个Dual Interface基本上将vtable与dispinterface在记忆体中的结构组合在一起,允许使用任一种介面的样式来呼叫method。然而dual Interface仍是自动化相容的,以便适用於Visual Basic与Java。事实上,使用Visual Basic建立的COM物件预设便是使用dual Interface来显露它们的method。</span><span id=Layer43></font></p><p><font size=2 color=#3c3c3c face=arial>第叁个介面类型,dual interface,合并了前两种介面类型</span><span id=Layer44></font></p><hr><font face=Arial Black color=#3e77d7 size=3><b></b></font><p><font size=2 color=#3c3c3c face=arial>第叁个介面类型,dual interface,合并了前两种介面类型</span><span id=Layer45></font></p><hr><p><font size=2 color=#3c3c3c face=arial><font size=2 face=arial color=#3e80d7><b> 辨识介面</span><span id=Layer46> </b></font>若要呼叫特定介面上的method,客户端必须取得指向某个执行中的COM物件其介面的指标。正确完成这个动作的过程将於稍後描述,不过很明显的,客户端若要取得这个指标得靠某些方式来辨识自己感兴趣的介面。换句话说,介面必须拥有名称。</span><span id=Layer47></font></p><p><font size=2 color=#3c3c3c face=arial>每一个介面要有名称</span><span id=Layer48></font></p><hr><font face=Arial Black color=#3e77d7 size=3><b></b></font><p><font size=2 color=#3c3c3c face=arial>每一个介面要有名称</span><span id=Layer49></font></p><hr><p><font size=2 color=#3c3c3c face=arial>在COM中,每个介面都拥有两个名称。其中一个名称是让人使用的,因此它只是一个字元字串。根据命名原则,COM介面的名称都是以字母「I」开始的。举例来说,前面提到的订单物件可能有一个类似IOrderEntry与IOrderStatistics的名称。当你谈论到COM物件,第一种样式的名称让人很容易记得。</span><span id=Layer50></font></p><p><font size=2 color=#3c3c3c face=arial>人类可以读取的介面通常以「I」开头</span><span id=Layer51></font></p><hr><font face=Arial Black color=#3e77d7 size=3><b></b></font><p><font size=2 color=#3c3c3c face=arial>人类可以读取的介面通常以「I」开头</span><span id=Layer52></font></p><hr><p><font size=2 color=#3c3c3c face=arial>然而,这类介面的名称并不是唯一的。有可能两个不同的人(也完全地合法)定义两个不同的介面,而名称都是称为,IMyInterface。为了要取得正确介面的介面指标,客户端必须有某种方式来区分某个特定的介面。因此,每一个介面都需要有一个唯一可供辨别的名称。</span><span id=Layer53></font></p><p><font size=2 color=#3c3c3c face=arial>因为第二类的名称必需是唯一的,对使用者来说它并没有太亲切的名称,它是要让软体使用的。因此每一个COM介面都指派一个值,也就是所谓的全域唯一识别码(Universally Unique Identifier,UUID)或称Globally Unique Identifier (GUID)。(这些术语通常交互使用,不过在本书中主要使用的是後者。) GUID是一个16位元组的值,在时间与空间上都是唯一的。指定到一个特定介面的GUID就是所谓的介面识别码(interface identifier,IID)。一旦一个IID(也就是一个GUID)指定到一个介面,这个IID就可以永远用来辨识这个介面。</span><span id=Layer54></font></p><p><font size=2 color=#3c3c3c face=arial>每个介面也拥有一个全域唯一的IID</span><span id=Layer55></font></p><hr><font face=Arial Black color=#3e77d7 size=3><b></b></font><p><font size=2 color=#3c3c3c face=arial>每个介面也拥有一个全域唯一的IID</span><span id=Layer56></font></p><hr><p><font size=2 color=#3c3c3c face=arial>因为GUID是全域唯一的,所以每当你要定义一个新介面时,不能单单只产生一个GUID。有许多不同的方式可以取得GUID。最困难也最不常用的方式便是从Microsoft要求一个GUID。最简单的方式便是在你的电脑上执行一个简单的软体工具。每当使用这个工具时,它便会产生一个新的GUID,而且保证是全域唯一的。GUID另一个重要的应用是当做IID,它们也可以用来当做其他东西的命名,我们将会看得到的。</span><span id=Layer57></font></p><p><font size=2 color=#3c3c3c face=arial>GUID一向都是使用软体工具建立的</span><span id=Layer58></font></p><hr><font face=Arial Black color=#3e77d7 size=3><b></b></font><p><font size=2 color=#3c3c3c face=arial>GUID一向都是使用软体工具建立的</span><span id=Layer59></font></p><hr><p><font size=2 color=#3c3c3c face=arial>为何能让在不同台机器上独立执行的相同软体,确能够确保软体产生的值都不相同呢?答案就在GUID所包含的东西。它的主要组成份子包含一个时间戳记(timestamp)指明建立的时间,以及安装在GUID产生的这台电脑之网路卡的Medium Access Control (MAC)位址。 时间戳记能确保在这台机器上产生的每个GUID都是不同的,而MAC位址确保两个同时产生的GUID不会相同,因为MAC位址是由一个集中的机构制定,每块网卡的MAC位址在这个世界上都是唯一的。这使得GUID的产生相当容易,但仍可以确保唯一性。若要论及所牵涉到某些公司的安全性,则Windows 2000可以产生GUID,将MAC位址再做个包装,而非全封不动地出现在GUID。</span><span id=Layer60></font></p><hr><font face=Arial Black color=#3e77d7 size=3><b>附注 </b></font><p><font size=2 color=#3c3c3c face=arial>为何能让在不同台机器上独立执行的相同软体,确能够确保软体产生的值都不相同呢?答案就在GUID所包含的东西。它的主要组成份子包含一个时间戳记(timestamp)指明建立的时间,以及安装在GUID产生的这台电脑之网路卡的Medium Access Control (MAC)位址。 时间戳记能确保在这台机器上产生的每个GUID都是不同的,而MAC位址确保两个同时产生的GUID不会相同,因为MAC位址是由一个集中的机构制定,每块网卡的MAC位址在这个世界上都是唯一的。这使得GUID的产生相当容易,但仍可以确保唯一性。若要论及所牵涉到某些公司的安全性,则Windows 2000可以产生GUID,将MAC位址再做个包装,而非全封不动地出现在GUID。</span><span id=Layer61></font></p><hr><p><font size=2 color=#3c3c3c face=arial><font size=2 face=arial color=#3e80d7><b> 定义介面</span><span id=Layer62> </b></font>一个COM介面是一个相当简单的概念,它只定义了客户端需要知道以便呼叫method的东西。因为COM使用许多方式来定义COM介面,所以你有许多种选择。定义介面最正式也最强大的方式便是使用COM的介面定义语言(Interface Definition Language,IDL)。IDL的语法是由C衍生的,而COM的IDL事实上是基於Open Group的Distributed Computing Environment (DCE)定义的IDL。</span><span id=Layer63></font></p><p><font size=2 color=#3c3c3c face=arial>IDL可以用来定义介面</span><span id=Layer64></font></p><hr><font face=Arial Black color=#3e77d7 size=3><b></b></font><p><font size=2 color=#3c3c3c face=arial>IDL可以用来定义介面</span><span id=Layer65></font></p><hr><p><font size=2 color=#3c3c3c face=arial>若前述的订单项目的介面是一个vtable介面,它的IDL看起来可能如下:</span><span id=Layer66></font></p><div style="background-color: #D7D7D7;"><font face=Arial size=3><pre>[ object,, uuid(A7CD0D00-1827-11CF-9946-493655354000)]
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -