第6章 关于HRESULT、GUID、注册表及其他细节
1、HRESULT值的结构:
| | 15bits设备代码 | 16bits返回代码 |
|__|_________________________|______________________________|
31 30 16 15 0
2、常用的HRESULT值:
S_OK: 函数成功。在某些情况下,它还表示函数返回了一个布尔真值。S_OK被定义为0。
NOERROR: 同S_OK相同。
S_FALSE: 函数成功并返回一个布尔假值。S_FALSE被定义为1。
E_UNEXPECTED: 无法预知的失败。
E_NOIMPLE: 成员函数未被实现。
E_NOINTERFACE: 组件不支持所请求的接口。此值将由WueryInterface函数返回。
E_OUTOFMEMORY: 组件无法分配所需的内存。
E_FAIL: 没有指定的失败。
注意S_OK和S_FALSE与C++中的true和false的差别。
3、一般不能直接将HRESULT值同某个成功代码(如S_OK)进行比较以决定某个函数是否成功也不能直接将其同某个失败代码(如E_FAIL)进行比较以决定函数调用是否失败。应该使用SECCEEDED和FAILED宏。
HRESULT hr = CoCreateInstance(...);
if(FAILED(hr))
return ;
hr = pI->QueryInterface(...);
if(SUCCEEDED(hr))
{
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);
7、GUID是英文Globally Unique Identifier(全局唯一标识符)的首字母缩写。IID是一个128比特(16)字节的一个GUID结构。
8、生成GUID :UUIDGEN.EXE和GUIDGEN.EXE
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命名约定:
<Program>.<component>.<version>
17、从ProgID到CLSID的转换:COM库函数:CLSIDFromProgID和ProgIdFromCLSID:
CLSID clsid;
CLSIDFromProgID("****.****.****",&clsid);
18、自注册:DLL一定要输出下边两个函数:
STDAPI DllRegisterServer();
STDAPI DllUnregisterServer();
用户可以使用程序REGSVR32.EXE来注册某个组件,它实际上是通过上述函数来完成组件的注册的。
19、组件类别使开发人员能够使开发人员无需创建组件实例就能决定它是否特工所需接口。一个组件类别实际上就是一个接口集合,该集合将被分配给一个GUID,此GUID此时被称做是CATID。对于某个组件而言,若它实现了某个组件类别的所有接口,那么它可以将其注册成该组件类别的 一个成员。这样,客户就能够通过从注册表中选择只属于某个特定组件类别的组件而准确找到它所需的组件。
20、组件类别的用途:指定某个组件必须实现的接口集合;用于指定组件需要其客户提供的接口集合。
组件类别的使用方法同C++中的抽象基类有点类似。一个抽象基类是一组其派生类所必须实现的函数的集合,所以可以说此派生类是相应抽象基类的一个特定实现。与此类似,一个组件类别是属于此类别的组件所必须实现的接口集合,属于某组件类别的组件是该组件类别的一个特定实现。
21、组件类别最吸引人的地方可能是开发人员无需自己完成注册表的处理。这些工作可以由Windows系统附带的一个名为Component Category Manager来完成。ICatRegister,ICatInformation。
22、在使用COM库中的其他函数(除CoBuildVersion外,此函数将返回COM库的版本号)之前,进程必须先调用CoInitialize来初始化COM库函数。当进程不再需要使用COM库函数时,必须调用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 = ::StringFromGUID2(CLSID_Component1,szCLSID,39);
传给StringFromGUID2的参数是一个Unicode串(即一个宽字符wchar_t类型的数组而不是char类型的字符数组)。在非Unicode的系统中,需要将结果转化为单字节字符(char)。为此,可以使用ANSI的wcstombs函数如下:
#ifndef _UNICODE
char szCLSID_single[39];
wcstombs(szCLSID_single,szCLSID,39);
#end if
26、
----------------------------------------------------------------------------
函数 用途
-----------------------------------------------------------------------------
StringFromCLSID 将CLSID转化成文本串
-----------------------------------------------------------------------------
StringFromIID 将IID转化成文本串
------------------------------------------------------------------------------
StringFromGUID2 将GUID转化成文本串。此串将被存放在调用者所分配的缓冲区中
------------------------------------------------------------------------------
CLSIDFromString 将一个文本串转化成CLSID
------------------------------------------------------------------------------
IIDFromString 将一个文本串转化成IID
------------------------------------------------------------------------------
27、一个注册组件类别并添加组件到组件类别的示例源码:CSDN我的资源中InsideCOM\CHAP06。