Introduction to COM - What It Is and How to Use It
By Michael Dunn
From www.codeproject.com
/**********原创翻译,转载请注明出处*********** 新手水平有限,欢迎批评指教 By FreeKid /
COM入门简介 -- 什么是COM , 怎样使用它 (2)
基本元素定义
让我们从头开始。简单的说,接口就是一组函数。这些函数被称作"方法"。接口的名称以大写字母'I'打头,例如"IShellLink"。在C++中,接口总被写成一个只含有纯虚函数的抽象基类。
接口可以从其它接口"继承"而来,它的机制就像C++中的单继承一样。而接口是不允许多重继承的。
COClass(Component Object Class)被包含在DLL或者EXE文件之中,并且它包含着一个或多个接口代码。COClass就是来实现这些接口的。COM对象是COClass在内存中的一个实例。虽然在通常情况下一个用来实现COM的类就是C++类,但要注意COM的"类"与C++中的"类"是不同的。
COM服务器是包含一个或多个COClass的二进制单位(DLL或者EXE)。
为了通知Windows COM服务器的位置,需要在注册表中"注册"(Registration)相应信息。同样,反注册(Unregistration)是用来消除那些注册信息的。
GUID(Globally Unique Identifier)是一串128位的数字。GUIDs是COM识别事物的一种方法,且这种方法是与语言无关的。每个接口和COClass都有一个GUID。GUIDs是全球唯一的,所以这就避免了命名冲突问题(只要你使用COM API来创建它们)。你有时也会发现UUID(Universally Unique Identifier)这个术语。然而,UUIDs和GUIDs在实际用途方面是相同的。
Class ID或者是CLSID,它们都是用来命名COClass的GUID。Interface ID或者IID,它们是用来命名接口的GUID。
其实有两个原因使得GUIDs在COM中使用的如此广泛:
1.GUIDs仅仅是一串数字,任何语言都可以处理它们。
2.当任何人在任意机器上创建GUIDs的时候,它都是唯一的。因此,COM开发者们可以自己创建GUIDs,而且不用担心有两名开发者选择了相同的GUID。这就免除了需要一个中央权威机构发放GUIDs的要求。
COM用HRESULT这个类型(整型)来返回错误或者成功的代码。尽管有个'H'前缀,但它并不是指向什么东西的"句柄"。稍后我将会详细的说明HRESULTs以及怎样测试它们。
最后,在做COM相关的事务时,与你进行交互的COM库其实是操作系统的一部分。通常情况下,COM库就被成为COM,但在这里为了避免误解,我没有这样做。
使用COM对象
每种语言都有其处理对象的方法。例如,在C++中你可以在栈上创建它们,或者使用new操作符动态的分配它们。COM库提供了它自己的一套对象管理方法,使得COM是与语言无关的。比较一下COM和C++的对象管理,区别如下:
创建一个新对象
在C++中,使用new操作符或直接在栈上创建对象。
在COM中,需要调用COM库中的API。
删除对象
在C++中,使用delete操作符或者让栈上的对象超出作用域,自动删除。
在COM中,所有的对象必须维持它们自己的引用计数。调用者必须在使用完对象之后通知对象,COM对象会在引用计数为0的时候在内存中自动删除。
事实上在创建和删除两个阶段之间,你必须去使用它。在创建COM对象的时候,你要告诉COM需要什么样的接口,请求获取它。如果对象创建成功,COM库会返回一个指向所请求接口的指针。之后你就可以像使用C++对象指针一样,通过这个指针来调用方法了。
创建COM对象
为了要创建COM对象并从中获得一个接口,你可以调用COM库的API函数 CoCreateInstance()。它的原型如下:
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
形参如下:
rclsid
COClass的CLSID。例如,你可以将CLSID_ShellLink传入创建一个COM对象用来建立快捷方式。
pUnkOuter
这个参数只有在聚合(aggregate)COM对象的时候才使用。聚合COM对象可以在已经存在的COClass中加入新的方法。在这里,只需要传入NULL表明我们不需要使用聚合。
dwClsContext
用来说明我们需要使用什么样的COM服务器。对这篇文章来说,我们总使用一个进程DLL作为最简单的服务器,因此传入CLSCTX_INPROC_SERVER。请注意,这里不应该使用CLSCTX_ALL(ATL的默认参数),因为它将在没有安装DCOM的Windows95系统上运行失败。
riid
你所需要接口的IID,例如,你可以传入IID_IShellLink用来获取IShellLink接口的指针。
ppv
接口指针的地址。COM库将通过这个参数返回所请求的接口。
当你调用CoCreateInstance()时,它将在注册表中查找CLSID,读取服务器的位置,并将服务器加载到内存中,最后创建一个你所申请的COClass实例。
下面是一个简单的调用,展示了一个CLSID_ShellLink对象并获取了COM对象的IShellLink接口指针。
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/1327ab569c1ae82736693a50b8e33378.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/0196c3df5ea9e936f21e9932cca91014.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/1327ab569c1ae82736693a50b8e33378.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/0196c3df5ea9e936f21e9932cca91014.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
首先我们声明了一个用来接收CoCreateInstance()返回结果的HRESULT和一个IShellLink指针。之后调用CoCreateInstance()函数创建一个新的COM对象。如果hr包含成功的代码,则SUCCEEDED宏返回TRUE,否则返回FALSE。相应的,也有一个用来检测失败代码的FAILED宏。
(未完待续)