长时间进行Windows编程的人一定对HRESULT特别熟悉,因为HRESULT作为一种函数的返回值类型曝光率实在太高了,可是你是否知道HRESULT到底是什么?为什么不直接使用简洁又亲切的BOOL作为函数的返回值类型呢?
单从名字上看,HRESULT似乎是指向函数结果的句柄,但是这种直观地猜想却是错误的。其实HRESULT的意义非常简单,它不是Handle to Result而是Here’s the Result。怎么样?H竟然是Here的缩写,相当与众不同吧!
为了对HRESULT一探究竟,我们可以打开WinError.h文件,看一看它对HRESULT的描述:HRESULT是COM函数/方法的返回值类型,它并非指向什么东西的句柄,而仅仅是一个32位的数值。HRESULT的32位被划分成多个区域,其结构如下所示:(博主对它进行了简化)
3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 98 7 6 5 4 3 2 1 0
+-+-----------------------------+-------------------------------+
|S| Facility | Status |
+-+-----------------------------+-------------------------------+
HRESULT被划分成三个区域:紧急程度(S: 1 bit)、设备编号(Facility Code: 15 bit)和状态编号(Status Code: 16 bit)。这样一来你会发现,仅需1个bit(即S)就可以表示函数调用成功与否,余下的比特可以发挥更大的作用,它们携带的信息远比一个BOOL变量多得多,它们可以详细地说明函数调用过程中的各种情况,而不仅仅标明函数调用成功或失败。
观察HRESULT的结构之后,很容易明白下面这条规则:判断函数调用成功与否不能简单地将返回值与S_OK或E_NOINTERFACE等进行比较,而是要使用FAILED/SUCCEEDED宏。在WinError.h文件中,FAILED/SUCCEEDED宏的定义如下所示:
#define SUCCEEDED(hr) (((HRESULT)(hr)) >= 0)
#define FAILED(hr) (((HRESULT)(hr)) < 0)
当然,你知道FAILED/SUCCEEDED宏的秘密后,也可以直接在程序中将hr与0进行大小判断。不过为了使得代码规范化,建议你仍然使用预定义宏FAILED/SUCCEEDED,而且他们并不会降低程序的效率。
光是会“读”HRESULT是不够的,有的时候我们还要能够主动创建它,从而使得我们定义的函数也能返回HRESULT类型的值。这可以通过使用预定义宏MAKE_HRESULT来实现,它在WinError.h文件的定义如下所示:
#defineMAKE_HRESULT(sev,fac,code) \
((HRESULT) (((unsigned long)(sev)<<31) | ((unsignedlong)(fac)<<16) | ((unsigned long)(code))))
在WinError.h文件中定义了很多错误码,能不能把函数的HRESULT类型返回值翻译成友好的错误提示呢?当然是可以的,我们可以通过调用Win32 API函数FormatMessage轻松实现,它的语法如下:
DWORDWINAPI FormatMessage( __in DWORD dwFlags, __in_opt LPCVOID lpSource, __in DWORD dwMessageId, __in DWORD dwLanguageId, __out LPTSTR lpBuffer, __in DWORD nSize, __in_opt va_list* Arguments );
第1个参数dwFlags是格式化选项,如FORMAT_MESSAGE_ALLOCATE_BUFFER表示要为lpBuffer分配内存空间、FORMAT_MESSAGE_FROM_SYSTEM表示在系统消息定义表中查询错误码的意义;第2个参数lpSource表示临时的消息定义表,如果第1个参数包含FORMAT_MESSAGE_FROM_SYSTEM,lpSource就不起作用;第3个参数dwMessageId也就是错误码,传入LRESULT类型的数值;第4个参数dwLanguageId即语言标识符,它由两部分组成Primary Language(语言类型)和Sublanguage(国家或地区),用宏MAKELANGID构造;第5个参数lpBuffer是输出的消息,也即错误码的翻译结果;第6个参数nSize表示为lpBuffer最少分配的字节数;第7个参数可选,一般不使用。举一个应用的例子,如下所示:
void*pMsgBuf;
::FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER| FROMAT_MESSAGE_FROM_SYSTEM,
NULL,
hr,
MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
(LPTSTR)&pMsgBuf,
0,
NULL);
读者不妨对应着上面的参数说明试着解释一下本例中各个实参的意义,更多细节可以参考MSDN的相关内容。