長時間進行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的相關內容。