资源描述:
[整理人醉意人间] [Emailsishui198] [QQ271718002] 说明在 Engine 的学习过程中,困难重重,原因是对好多概念好多思想不明白,于是就有了疯狂的 Google 搜 索,看到了不少好的文章,于是便将这些收录下来,再加上自己的一些心得,现在向大家分享,希望对大家有所 帮助,文章的出处我尽量给出链接,在很多博文中我将自己的一些观点也加入进去,还望作者原谅。Engine 中 涉及到 COM 的一些东西,我就从 COM 整理。 COM 篇 (-)Com 速成原理 一、 COM 编程思想--面向组件编程思想COP 众所周知,由 C 到 C,实现了由面向过程编程到面向对象编程的过渡。而 COM 的出现,又引 出了面向组件的思想。其实,面向组件思想是面向对象思想的一种延伸和扩展。因此,就让我们先来 回忆一下面向对象的思想吧。 面向对象思想是将所有的操作以及所操作的对象都进行归类由 Class 实现, 而它的目标是要尽量 提高代码的可重用性这也是面向对象相比面向过程最大的优点之一。比如,有两个程序 A和 B 都需 要对Class C的对象进行操作, 那么Class C的代码就可以重用了即A和B都可以使用Class C的代码。 但是,对于这一点,面向对象做得并不够好。还是举刚才的例子,程序 A和 B 都要对 Class C 的对象 进行操作,那么,程序 A和 B 的编程人员都必须将 Class C 的代码拷贝过来,然后重新编译一次,这 将是多么麻烦的事况且,如果 Class C 的代码没有公开,那这种重用就根本不可能实现了除非程序 A和 B 的编程人员和 Class C 的编程人员是同一个人或者团队,但这样局限性就相当大了。 由于面向对象的这些局限性,很多程序员就会想,如果我们编程需要重用别人的成果时,不需要 重新编译别人的代码那就好了。换句话说,我们要达到的目标是,直接重用别人的成果而不是重用别 人的代码。这样说也许很抽象,举个例子大家就会比较明白。比如将 Class C 的代码编译生成一个 dll, 那么当其他程序员想要重用 Class C 时, 就只需要在自己的程序中加载这个 dll 而不需要重新编译 Class C 的代码了这也就是组件必须要能动态链接的原因。正是这种思路引出了面向组件的编程思想。 下面,我就简单介绍一下面向组件的思想。在以前,应用程序总是被编写成一个单独的模块,就 是说一个应用程序就是一个单独的二进制文件。后来在引入了面向组件的编程思想后,原本单个的应 用程序文件被分隔成多个模块来分别编写,每个模块具有一定的独立性,也应具有一定的与本应用程 序的无关性。一般来说,这种模块的划分是以功能作为标准的。比如,一个网上办公管理系统,从功 能上说它需要包含网络通信、数据库操作等部分,我们就可以将网络通信和数据库操作的部分分别提 出来做成两个独立的模块。那么,原本单个的应用程序就分隔成了三个模块主控模块、通信模块和 数据库模块。而这里的通信模块和数据库模块还可以做得使其具有一定的通用性,那么其他的应用程 序也就可以利用这些模块了。这样做的好处有很多,比如当对软件进行升级的时候,只要对需要改动 的模块进行升级,然后用重新生成的一个新模块来替换掉原来的旧模块但必须保持接口不变,而其 他的模块可以完全保持不变。这样,软件升级就变得更加方便,工作量也更小。 说了这么多,总结一下面向组件编程思想,归结起来就是四个字模块分隔。这里的“分隔”有 [整理人醉意人间] [Emailsishui198] [QQ271718002] 两层含义,第一就是要“分”,也就是要将应用程序尤其是大型软件按功能划分成多个模块;第二就 是要“隔”,也就是每一个模块要有相当程度的独立性,要尽量与其他模块“隔”开。这四个字是面向组 件编程思想的精华所在,也是 COM 的精华所在理解了这四个字,也就真正理解了面向组件编程的 思想。这里说一点题外话,COM 其实是一套规范或者说一套标准,但是在我看来,COM 的核心还在 于它的思想,也就是面向组件编程思想。标准谁都能定,但是思想只有一个 二、COM 的优点 COM 的优点也就是面向组件编程思想的优点。 而面向组件编程思想有很多的优点, 上面所说的便 于软件升级只是其中之一。对于它的优点,我总结了一下,有下面几条 1、便于重用,使软件开发更快捷 2、便于软件升级 3、便于软件开发的分工协作 4、便于用户定制自己的应用 以上几点,第一和第二点都不用再多说了,前面讲面向组件编程思想的部分里面已经充分展示出 了这两点优点。在这里我解释一下第三和第四点。 如今的很多大型软件,都不可能由某一个人单独开发,甚至不会由某一个公司去单独开发。这是 因为现在的很多大型软件,综合性太强,涉及的面也太广。而一个人的精力是有限的,不可能学会这 么多方面的知识,也不可能掌握到这么多方面的编程技术,即使有可能,这样做的效率也是很低下的。 所以,通常的情况是分工协作。仍以前面提到的网上办公管理系统为例,这个系统分为了三个模块 主控模块、通信模块和数据库模块。由于这三个模块具有相当的独立性,那么就可以将现有的所有开 发人员分为三组,每一组负责一个模块。而这三组之间,只需要商量好相互间的接口就可以了。这样, 对于每一个开发人员来说,就不需要掌握所有的编程技术,甚至不需要了解其他模块的具体实现,而 软件仍然能有效的开发成功。这就是所谓的便于软件开发的分工协作了。 除此之外,如果一个大型的软件希望允许用户在一定程度上定制自己的应用,那么 COM 也是最 好的选择。比方说一个软件由两个模块组成,模块 A和模块 B,现在软件的开发商希望给予用户一定 的灵活性,希望可以允许用户自己定制模块 B 来实现自己特定的应用,那么就只需要公开模块 B 的所 有接口;而用户自己编程实现模块 B 时也只需要实现了所有的这些接口就行了。当然,这里面还有很 多问题,比如 COM 组件的注册,这涉及到 COM 标准的一些细节,在这里不作讨论。 三、COM 中的几个重要概念 1、组件 其实只要你仔细阅读了前面的部分,组件的概念应该已经很清楚了。这里所说的组件,就是前面 反复在讨论的所谓“模块”。现在我只想强调一下组件需要满足的一些条件。首先是封装性,组件必须 向外部隐藏其内部的实现细节,使从外部所能看到的只是接口。然后是组件必须能动态链接到一起, 而不必像面向对象中的 Class 一样必须重新编译。 2、接口 由于组件向外部隐藏了其内部的细节, 因此客户要使用组件时就必须通过一定的机制, 也就是说要 通过一定的方法来实现客户与组件之间的通信,这就需要接口。所谓接口就是组件对外暴露的、向外 [整理人醉意人间] [Emailsishui198] [QQ271718002] 部客户提供服务的“连接点”。外部的客户见不到组件内部的细节,它所能看到的只是接口,客户也是 通过接口来获取组件提供的服务。这有点像 OSI网络协议分层模型,每一层就像一个组件,它内部的 实现细节对于其他层是不可见的;而每一层通过“服务接入点”向其上层提供服务,这就像这里所说的 接口。一般来说,接口总是固定的,也是公开的。组件的开发人员要实现这些接口,而客户则通过接 口获得服务。正是接口的这种固定和公开,才使得组件和客户能够在不了解对方的情况下达成一致。 3、客户 这里所说的客户不是指使用软件的用户,而是指要使用某一个组件的程序或模块。也就是说,这 里的客户是相对组件来说的。我们用Exploring ArcObjects (我相信学习 AE 或者 AO 的朋友都看过 或者知道这本书,关于这本书的下载,Google 下就可以了,这本书是英文的,因为看起来比较麻烦,曾 经有打印这本书的冲动,但是 Money的问题)上的一幅图说明下 四、COM 的实现原理与雏形 COM 编程的一个重要特点就是要模块化,说得具体一些,就是要将客户和组件分隔开来,而客户和组 件之间又是通过接口来通信的。下面,我就介绍一下 COM 是怎样将客户与组件分隔开来,又是怎样 利用接口来实现客户与组件间的通信的。 首先我要讲讲接口。COM 中的接口实际上是一个函数地址表,当组件实现了这个接口后,这个函 数地址表中就填满了组件所实现的那些接口函数的地址。而客户也就是通过这个函数地址表获得组件 中那些接口函数的指针, 从而获得组件所提供的服务的。 从某种意义上说, 我们可以把接口理解为 c 中的虚拟基类;或者说,在 c中可以用虚拟基类来实现接口这是因为 COM 中规定的接口的存储结 构,和 c中的虚拟基类在内存中的结构是一致的。其存储结构如下图 [整理人醉意人间] [Emailsishui198] [QQ271718002] Vtbl 指针指向一个虚函数表,而这个虚函数表的表项就是指向这些虚函数的指针。 接口有了,那么组件又是怎样实现接口的呢实际上,如果用虚拟基类来实现接口,那么组件就 是对这个虚拟基类的继承。大家知道,当某个类继承于一个虚拟基类的时候,它就要实现这个虚拟基 类里声明的虚函数,这就正好与组件实现接口这一点相吻合。举一个例子来说明,有一个接口 InterfaceA,组件 ComponentB 要实现这个接口,那么就可以这样用 c语言来描述 //接口 Class InterfaceA { virtual void Fun10; virtual void Fun20; }; //实现了接口 InterfaceA 的组件 Class ComponentB public InterfaceA { virtual void Fun1 { printf“Fun1“n“; } virtual void Fun2 { printf“Fun2“n“; } }; 而客户只需要得到一个指向 ComponentB实体的 InterfaceA指针就可以获得 ComponentB 组件的服 务了 //使用了组件 ComponentB 的客户 ComponentB CB; [整理人醉意人间] [Emailsishui198] [QQ271718002] InterfaceA *pIA //获得指向 ComponentB 实体的 InterfaceA 指针,以下客户就可以只通过接口来 获取组件的服务 pIA-Fun1; pIA-Fun2; 但是我们注意到,这样做组件 ComponentB 和客户还是没有被完全分隔开。因为在客户代码里需 要创建 ComponentB 实体,这对于只能看到接口而对组件一无所知的客户来说,是不可以接受的比如 客户不会知道组件的类名叫 ComponentB。 解决这个问题的方法是在实现组件的动态链接文件比如 dll 文件里创建组件的实体,而不是在客户代码里创建组件实体。通常组件都是以 dll 的形式出现的,而 在实现组件的 dll 里都会实现一个叫 CreateInstance 的函数,这个函数可以被外部的客户调用。它返回 一个接口的指针,当客户调用这个函数后就能够获得指向组件实体的接口指针了。它的实现也很简单 //在实现组件 ComponentB 的 dll 里 InterfaceA *CreateInstance { ComponentB CB; InterfaceA *pIA return pIA; } (注在 DirectX 编程时,很多时候都会用到 CreateXXX 之类的函数,道理就是这样。 ) 当然,真正的 CreateInstance 函数没有这么简单,我上面的代码只是一个简单的模拟。有个 CreateInstance 函数之后,客户代码就变成了 //使用了组件 ComponentB 的客户 InterfaceA *pIACreateInstance; //获得指向 ComponentB 实体的 InterfaceA指针,以下客户就可以只通 过接口来获取组件的服务 pIA-Fun1; pIA-Fun2; 这样,组件和客户就完全被分隔开了,而连接它们的只有接口以及一个 CreateInstance 的函数。 以上就是 COM 的基本原理了。当然,我前面也说了,COM 其实是一套规范,它定义了很多标准, 比如COM规定每个接口都必须继承于一个叫IUnknown的接口。 我这里基本上没有提及它的这些标准, 只是希望能通过对它进行一个简单的模拟来说清楚它的实现原理。下面就给出我模拟 COM 机制实现 的一套 COM 的雏形,希望能对大家理解 COM 有帮助。 1、实现了组件 ComponentB 的 ComponentDll.dll //Interface.h [整理人醉意人间] [Emailsishui198] [QQ271718002] //接口 Class InterfaceA { public virtual void Fun10; virtual void Fun20; }; //Component.h //组件实现了接口 InterfaceA Class ComponentB public InterfaceA { public virtual void Fun1 { printf“Fun1“n“; } virtual void Fun2 { printf“Fun2“n“; } }; //ComponentDll.cpp //CreateInstance 函数 ComponentB instance; extern “C“ _declspecdllexport InterfaceA *CreateInstance { InterfaceA *pIA return pIA; } 2、客户 Client. //Client.cpp include “Interface.h“ pragma commentlib,“ComponentDll“ int mainint argc, char* argv[] { [整理人醉意人间] [Emailsishui198] [QQ271718002] InterfaceA *pIA0; pIACreateInstance; ifpIA0 pIA-Fun1; return 0; } (二)COM 编程技术基础 第第 1 章章 组件组件 1、COM,即组件对象模型,是关于如何建立组件以及如何通过组件建构应用程序的一个规范。 2、组件的优点应用程序可随时间的流逝而发展变化;定制应用程序;组件库;分布式组件。 3、对组件的需求组件必须动态连接;必须隐藏其内部实现细节。 4、COM 组件是以 Win32 动态链接库(DLLs)或可执行文件(s)的形式发布的可执行代码组成 的。遵循 COM 规范编写的组件将能够满足对组件家够的所有需求。COM 组件是动态链接的,COM 使 用 DLL 将组件动态链接起来。对于 COM 组件的封装是很容易的。COM 组件按照一种标准的方式来宣 布他们的存在。COM 组件是一种给其他应用程序提供面向对象的 API或服务的极好方法。 5、COM 并不是一种计算机语言。 6、将 COM 同 DLL 相提并论是不合适的。实际上 COM 使用了 DLL 来给组件提供动态链接的能力。 7、COM 并不是像 Win32API那样的函数集,它更主要的是一种编写能够按面向对象 API形式提供服 务的组件的方法。 8、COM 并不是类似于 MFC 这样的 C类库。COM 给开发人员提供的是一种开发与语言无关的组件库 的方法,但 COM 本身并没有提供任何实现。 9、COM 具有一个被称作是 COM 库的 API,它提供的是对所有客户及组件都非常有用的组件管理服务 第第 2 章章 接口接口 1、在 COM 中接口就是一切。 (1)接口可以保护系统免首外界变化的影响。 (2)接口可以使客户用同样的方式来处理不同的组件。 2、 (1)COM 接口在 C中是用纯抽象基类实现的。 (2)一个 COM 组件可以提供多个接口。 (3)一个 C类可以使用多继承来实现一个可以提供多个接口的组件。 3、类并非组件。 4、接口并非总是继承的。对接口的继承只不过是一种实现细节而已。除了可以使用一个类来实 现几个不同的接口外,还可以用单个的类来实现每一个接口再使用指向这些类的指针。 5、组件可以支持任意数目的接口。为支持多重接口,可以使用多重继承。支持多重接口的组件 [整理人醉意人间] [Emailsishui198] [QQ271718002] 可以被看作是接口的集合。 6、COM 接口的不变性、多态以及接口继承。 (1)一旦公布了一个接口,那么它将永远保持不变。当对组件进行升级时,一般不会修改已有 的接口,而是加入一些新的接口。 (2)多态指的是可以按同一种方式来处理不同的对象。 7、虚拟函数表(vtbl包含一组指向虚拟函数实现的指针。 定义一个纯抽象基类也就是定义了相应的内存结构。但此内存只是在派生类中实现此抽象基类时 才会被分配。当派生类继承一个抽象基类时,它将继承此内存结构。 8、在 COM 中,对一个组件的访问只能通过函数完成,而绝不能直接通过变量。 9、接口的真正的威力在于继承此接口的所有类均可以被客户按同一方式进行处理。 (注接口和类的区别接口可以看成一个特殊的类的形式,除了不能实例化一个对象外,能实现类能完 成的一切功能) 。 第第 3 章章 QueryInterface 函数函数 1、接口查询 客户同组件的交互都是通过一个接口完成的。在客户查询组件的其他接口时,也是通过接口完成 的。这个接口就是 IUnknown。 (注为什么会有 QueryInterface,因为一个类可以实现多个接口,这个函数就是为了访问同一个类中的 不同接口)用Exploring ArcObjects上的一幅图说明下 IUnknown 接口的定义包含在 Win32 SDK 中的 UNKNOWN.H 头文件中。 interface IUnknown { virtual HRESULT _stdcall QueryInterfaceconst IID virtual ULONG _stdcall AddRef 0; virtual ULONG _stdcall Release 0; } 在 IUnknown 中定义了一个名为 QueryInterface 的函数。客户可以调用 QueryInterface 来决定组件 是否支持某个特定的接口。 2、所有的 COM 接口都需要继承 IUnknown。 [整理人醉意人间] [Emailsishui198] [QQ271718002] 3、由于所有的 COM 接口都继承了 IUnknown,每个接口的 vtbl 中的前三个函数都是 QueryInterface,AddRef 和 Release。若某个接口的 vtbl 中的前三个函数不是这三个,那么它将不 是一个 COM 接口。由于所有的接口都是从 IUnknown 继承的,因此所有的接口都支持 QueryInterface.因此组件的任何一个接口都可以被客户用来获取它所支持的其他接口。 4、非虚拟继承注意 IUnknown 并不是虚拟基类,所以 COM 接口并不能按虚拟方式继承 IUnknown, 这是由于会导致与 COM 不兼容的 vtbl。若 COM 接口按虚拟方式继承 IUnknown,那么 COM 接口的 vtbl 中的头三个函数指向的将不是 IUnknown 的三个成员函数。 5、一个 QuertyInterface 可以用一个简单的 if-then-else 语句实现,但 case 语句是无法用的,因 为接口标识符是一个结构而不是一个数。 6、多重类型及类型转换 7、QueryInterface 的规则 (1)QueryInterface 返回的总是同一 IUnknown 指针。 (2)若客户曾经获取过某个接口,那么它将总能获取此接口。 (3)客户可以再次获取已经拥有的接口。 (4)客户可以从任何接口返回到起始接口。 (5)若能够从某个借口获取某特定接口,那么可以从任意接口都将可以获取此接口。 8、接口的 IID 决定了它的版本。当改变了下列条件中的任何一个时,就应给新接口指定新的 ID (1)接口中函数的数目。 (2)接口中函数的是顺序。 (3)某个函数的参数。 (4)某个函数参数的顺序。 (5)某个函数参数的类型。 (6)函数可能的返回值。 (7)函数参数的含义。 (8)接口中函数的含义。 9、避免违反隐含和约 (1)使接口不论在其成员函数怎么被调用都能正常工作。 (2)强制客户按一定的方式来使用此接口并在文档中将这一点说明清楚。 (注我觉得以下几章都没有必要,但是为了保证原博客的完整,就附上) 第第 4 章章 引用计数引用计数 1、生命期控制 IUnknown 的另外两个成员函数 AddRef 和 Release 的作用就是给客户提供一种让它指示何时处理完 一个接口的手段。 2、AddRef 和 Release 实现的是一种名为引用计数的内存管理技术。 引用计数是使组件能够自己将自己删除的最简单同时也是效率最高的方法。 COM 组件将维护一个称做是引用计数的数值。当客户从组件取得一个接口时,此引用计数值将增 1 [整理人醉意人间] [Emailsishui198] [QQ271718002] 。当客户使用完某个接口后,组件的引用计数值将减 1。当引用计数值为 0 时,组件即可将自己从 内存中删除。 3、正确使用引用计数规则 (1)在返回之前调用 AddRef。对于那些返回接口指针的函数,在返回之前应用相应的指针调用 AddRef。这些函数包括 QueryInterface 及 CreateInstance。这样当客户从这种函数得到一个接口 后,它将无需调用 AddRef。 (2)在使用完接口之后调用 Release。在使用完某个接口之后应调用此接口的 Release 函数。 (3)在赋值之后调用 AddRef。在将一个接口指针赋给另外一个接口指针时,应调用 AddRef。换 句话说,在建立接口的另外一个引用之后应增加相应组件的引用计数。 (4)在客户看来,引用计数是处于接口级上而不是组件级上的。 (5)为什么选择为每一个接口单独维护一个引用计数而不是针对整个组件维护引用计数(1)使 程序调试更为方便; (2)支持资源的按需获取。 6、AddRef } ULONG _stdcall Release { ifInterlockedDecrement return 0; } return m_cRef; } 7、当建立一个新组件时,应建立一个对此组件的引用。因此创建组件时,在将指针返回给客户 之前,应该增大组件的引用计数值。这使程序员可以不必在调用 CreateInstance 或 QueryInterface 之后记着去调用 AddRef。 8、引用计数规则优化 (1)输出参数规则任何在输出参数中或作诶返回值返回一个新的接口指针的函数必须对此接 口指针调用 AddRef。 (2)输入参数规则对传入函数的接口指针,无需调用 AddRef 和 Release,这是因为函数的生命 期嵌套在调用者的生命周期内。 (3)输入-输出函数规则对于用输入-输出参数传递进来的接口指针,必须在给它赋另外一个 接口指针之前调用其 Release。在函数返回之前,还必须对输出参数中所保存的接口指针调用 [整理人醉意人间] [Emailsishui198] [QQ271718002] AddRef。如 void ExchangeForCachedPtr int i, IX **ppIX { *ppIX-Fx; //Do something with in-parameter. *ppIX-Release;//Release in parameter. *ppIX g_Cache;//Get cached pointer. *ppIX-AddRef;//AddRef pointer. *ppIX-Fx;//Do something with out-parameter. } (4)局部变量规则对于局部复制的接口指针,由于它们只是在函数的生命周期内才存在,因 此无需调用 AddRef 和 Release。 (5)全局变量规则对于保存在全局变量中的接口指针,在将其传递给另外一个函数之前,必 须调用其 AddRef。由于此变量是全局的,因此任何函数都可以通过调用其 Release 来终止其生命 期。对于保存在成员变量中的接口指针,也应按此种方式进行处理。因为类中的任何成员函数都 可以改变次中接口指针的状态。 (6)不能确定时的规则对于任何不能确定的情形,都应调用 AddRef 和 Release 对。 第第 5 章章 动态链接动态链接 1、从 DLL 中输出函数用 extern “c“标记。 2、在使用 VC 时,可以用 DUMPBIN。 来得到某个 DLL 中所输出的符号的清单。如下面的命令 dumpbin -exports Cmpnt1.dll 3、装载 DLLLoadLibrary以被装载的 DLL 的名称作为参数并返回一个指向所装载的 DLL 的句柄。 win32 的 GetProcAddress 函数可以使用此句柄以及待用的函数的名称,然后返回一个指向次函数 的指针。 4、使用 DLL 实现组件的原因DLL 可以共享它们所链入的应用程序的地址空间。 第 6 章 关于 HRESULT、GUID、注册表及其他细节 1、HRESULT 值的结构 _________________________________________________________ | | | | | | 15bits 设备代码 | 16bits 返回代码 | |__|_________________________|______________________________| 31 30 16 15 0 2、常用的 HRESULT 值 3、一般不能直接将 HRESULT 值同某个成功代码(如 S_OK)进行比较以决定某个函数是否成功也不 [整理人醉意人间] [Emailsishui198] [QQ271718002] 能直接将其同某个失败代码(如 E_FAIL)进行比较以决定函数调用是否失败。应该使用 SECCEEDED 和 FAILED 宏。 HRESULT hr CoCreateInstance...; ifFAILEDhr return ; hr pI-QueryInterface...; ifSUCCEEDEDhr { pIX-Fx; pIX-Release; } pI-Release; 4、当前所定义的设备代码 FACILITY_WINDOWS 8 FACILITY_STORAGE 3 FACILITY_SSPI 9 FACILITY_RPC 1 FACILITY_WIN32 7 FACILITY_CONTROL 10 FACILITY_NULL 0 FACILITY_ITF 4 FACILITY_DISPATCH 2 FACILITY_CERT 11 5、关于定义自己的 HRESULT 的一些一般性规则 (1)不要将 0X0000 及 IX01FF 范围内的值作为返回代码。这些值是为 COM 所定义的 FACILITY_ITF 代 码而保留的。只有遵循这一规则,才不致使用户自己定义的代码同 COM 所定义的代码相混淆。 (2)不要传播 FACILITY_ITF 错误代码。 (3)尽可能使用通用的 COM 成功及失败代码。 (4)避免定义自己的 HRESULT,而可以在函数中使用一个输出参数。 6、用 MAKE_HRESULT 宏来定义一个 HRESULT 值,此宏可根据所提供的严重级别、设备代码及返回 代 码生成一个 HRESULT 值。如 MAKE_HRESULT(SEVERITY_ERROR,FACILITY_ITF,100) ; [整理人醉意人间] [Emailsishui198] [QQ271718002] 7、GUID 是英文 Globally Unique Identifier全局唯一标识符)的首字母缩写。IID 是一个 128 比 特(16)字节的一个 GUID 结构。 8、生成 GUID UUIDGEN. 和 GUIDGEN. 9、GUID 的比较操作符;等价函数 IsEqualGUID,IsEqualIID,IsEqualCLSID。 10、将 GUID 作为组件标识符 11、由于一个 GUID 值占用了 16 个字节,因此一般不用值传递 GUID 参数。而大量使用的是按引用传 递。 12、COM 只使用了注册表的一个分支HKEY_CLASSES_ROOT。 13、注册表 CLSID 是一个具有如下格式的串 {********-****-****-****-************} 14、CLSID 关键字的子关键字 InprocServer32 关键字的缺省值是组件所在的 DLL 文件名称。 15、一些特殊关键字 (1)AppID此关键字下的子关键字的作用是将某个 APPID(应用程序 ID)隐射成某个远程服务 器名称。分布式 COM 将用到此关键字。 (2)组见类别注册表的这一分支可以将 CATID(组件类别 ID)映射成某个特定的组件类别。 (3)Interface用于将 IID 映射成与某个接口相关的信息。 (4)Licenses保存授权使用 COM 组件的一些许可信息。 (5)TypeLib类型库关键字所保存的是关于接口成员函数所用参数的信息等。 16、ProgID 命名约定 .. 17、从 ProgID 到 CLSID 的转换COM 库函数CLSIDFromProgID 和 ProgIdFromCLSID CLSID clsid; CLSIDFromProgID“****.****.****“, 18、自注册DLL 一定要输出下边两个函数 STDAPI DllRegisterServer; STDAPI DllUnregisterServer; 用户可以使用程序 REGSVR32. 来注册某个组件,它实际上是通过上述函数来完成组件的注册的. 19、组件类别使开发人员能够使开发人员无需创建组件实例就能决定它是否特工所需接口。一个 组件类别实际上就是一个接口集合,该集合将被分配给一个 GUID,此 GUID 此时被称做是 CATID。 对于某个组件而言,若它实现了某个组件类别的所有接口,那么它可以将其注册成该组件类别的 一个成员。这样,客户就能够通过从注册表中选择只属于某个特定组件类别的组件而准确找到它 所需的组件。 20、组件类别的用途指定某个组件必须实现的接口集合;用于指定组件需要其客户提供的接口 集合。 22、在使用 COM 库中的其他函数(除 CoBuildVersion 外,此函数将返回 COM 库的版本号)之前,进 程必须先调用 CoInitialize 来初始化 COM 库函数。当进程不再需要使用 COM 库函数时,必须调用 [整理人醉意人间] [Emailsishui198] [QQ271718002] CoUninitialize。对每一个进程,COM 库函数只需初始化一次。这并不是说不能多次调用 CoInitailize,但需保证每一个 CoInitialize 都有一个相应的 CoUnoinitialize 调用。当进程已 经调用过 CoInitialize 后,再次调用此函数所得到的返回值将是 S_FALSE 而不再是 S_OK. 23、OLE 是建立在 COM基础之上的,它增加了对类型库、剪贴板、拖放、ActiveX 文档、自动化以 及 ActiveX 控件的支持。在 OLE 库中包含对这些特性的额外的支持。在需要使用这些特性时,应调 用 OleInitailize 及 OleUninitialize,而不是 CoInitailize 和 CoUninitialize。Ole*函数将调用 Co*函数。但若程序中没有用到那些额外的功能,使用 Ole*将会造成资源的浪费。 24、COM 中分配和释放内存的标准方法任务内存分配器。使用此分配器,组件可以给客户提供 一块可以由客户删除的内存。可在多线程应用程序中使用。 一些方便的函数 void *CoTaskMemAlloc ULONG cb //size in bytes of block to be allocated ; void CoTaskMemFree void *pv //pointer to memory block to be freed ; 25、StringFromGUID2 可以将某个 GUID 转换成一个字符串 wchar_t szCLSID[39]; int r StringFromGUID2CLSID_Component1,szCLSID,39; 传给 StringFromGUID2 的参数是一个 Un
展开阅读全文