Inside COM读书笔记-----关于HRESULT、GUID、注册表及其它细节
1. HRESULT
HRESULT是一个可以分成三个域的32位值,
注意:S_FALSE被定义为1而S_OK被定义为0,这一点同C/C++变成原则正好相反。
HRESULT值中16到30这15个比特位包含的时设备代码,设备代码标识的时可以返回HRESULT中返回代码的操作系统部分。当前定义的:
HRESULT值的使用
- 成功的代码有多个,失败的代码也有多个
一个函数在各种情况下返回的状态太代码通常包含多个成功代码及多个失败代码。这就是为什么要使用SUCCEEDED及FAILED宏的原因。一般不直接将HRESULT值同某个成功代码(如S_OK)| 失败代码(如S_FAILE)进行比较以决定某个函数是否成功,应使用SECCEEDED及FAILED宏判断
- 失败代码可能会发生变化
其他人可能会定义新的HRESULT错误代码,客户程序也可能会遇到相应的错误。由于客户使用的组件可能会发生变化,因此组件返回的错误代码也可能会发生变化。
2. GUID
为什么要使用GUID来标识接口而不是用一个长整数呢?问题的实质并不是在于我们可以标识多少个接口,而在于怎样保证每一个接口标识符都是唯一的。
GUID的声明和定义
可以使用ObjBase.h中的DEFINE_GUID宏来定义
#ifndefINITGUID
#defineDEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
EXTERN_C const GUID FAR name
#else
#defineDEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
EXTERN_C const GUID name \
= { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }
#endif // INITGUID
在ObjBase.h中所定义的DEFINE_GUID将产生于下列类似的输出:
EXTERN_C const GUID FAR IID_IX
但是,如果在OBjBase.h之后包含INITGUID.H,那么DEFINE_GUID所产生的代码就如下:
//{32bb8320-b41b-11cf-a6bb-0080c7b2d682}
Extern “C” const IID IID_IX =
{0x32bb8320, 0xb41b, 0x11cf,
{0xa6,0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}} ;
GUID的比较
在ObjBase.H中定义了一个==操作符,也可以使用IsEqualGUID、IsEqualIID以及IsEqualCLSID。
3. windows注册表
注册表的组织
注册表是一个有许多元素构成的层次结构,每一个元素均被称作是一个关键字。每一个关键字可以包含一系列子关键字。一系列命名的值及/或一个未命名的值。子关键字也可以包含他的子关键字及值,但值却不能有子关键字或值。每一个值都有其相应的类型。注册表结构:
CLSID关键字结构
COM只使用了注册表的一个分支:HEKY_CLASSES_ROOT。在此关键字下,可以找到有CLSID关键字。在clsid关键字之下列有系统所有安装的组件的CLSID,注册表CLSID是一个具有如下格式的串:{32bb8320-b41b-11cf-a6bb-0080c7b2d682},对于关键字现在关心的时他的一个子关键字InprocServer32.此关键字的缺省值是组件所在的DLL文件名称
关于注册表的其他细节
在HKEY_CLASSES_ROOT地开头,列出的将是各种应用程序所注册的文件扩展名。在扩展名之后,可以看到许多其他的名字。此类名字的大多数被称为ProgID,表示是程序员定义的标识符。
- AppID-----此关键字下地子关键字的作用是将某个APPID(应用程序ID)映射成某个远程服务器名称。分布式COM将用到此关键字
- 组件类别----注册表的这一分支可以将CATID(组件类别ID)映射成某个特定的组件类别。
- Interface----用于将IID映射成与某个接口相关的信息。这些信息主要公寓在跨进程便捷使用接口的情况
- License-----License保持的时授权使用COM组件的一些许可信息
- TypeLib-----类型库关键字所保持的时关于接口成员函数所用参数的信息。
ProgID
ProgID指的时程序员给某个CLSID指定的一个程序员易记的名称。
- ProgID命名约定
ProgID具有如下的格式:
<Program>.<Component>.<Version>
比如:Visio.Drawing.4
- ProgID注册表格式
- 从ProgID到CLSID的转换
CLSID clsid;
CLSIDFromProgID(L"Primer.xxx.1",&clsid);
自注册
DLL要输出一下两个函数:
#defineSTDAPI EXTERN_C HRESULTSTDAPICALLTYPE
STDAPI DllRegisterServer();
STDAPI DllUnregisterServer();
用户可以使用程序RegSve32.exe来注册某个组件。它实际上通过调用上述那些函数来完成组件的注册的。
- DllRegisterServer的实现
通过直接调用注册表函数来完成的。API中有许多想注册表中加入新的关键字及删除某个关键字的函数。
RegOpenKeyEx
RegCreateKeyEx
RegSetValueEx
RegEnumKeyEx
RegDeleteKey
RegCloseKey
组件类别
组件类实际上就是一个接口集合,该集合将被分配给一个GUID,此GUID此时叫做CATID。对于某个组件而言,若它实现了某个组件类别的所有接口,那么他就可以将其注册成该组件类别的一个成员。客户就可以通过从注册表中选择只属于某个特定组件类别的组件而准备找到他所需要的组件
4. Com库
- COM库的初始化
在使用COM库的其他函数之前,进程必须先调用CoInitialize才初始化COM库函数。当进程不在需要使用COM函数时,必须调用CoUnInitialize
- 内存管理
CoTaskMemAlloc()
CoTaskMemFree();
- 将字符串转换成GUID
在注册表中包含的CLSID的字符串表示,因此需要一些函数来完成从CLSID到字符串的转换