一切從一個C++ 類庫頭文件開始,現在在做一個C++的項目,期間用到一個開源的界面庫DUILib(類似MFC),這個東西還不錯能很容易的寫出漂亮的界面,比如QQ的界面,可以去下載下來研究研究,地址:http://code.google.com/p/duilib/
廢話不多說,我比較困擾的是UIWebBrowser.h這個頭文件,雖然是C++寫的,但裏面包含太多大學C++課本以外的東西,第一遍看下來跟看天書一樣,裏面有很多的不惑,接下來我們一個一個解開。
首先看一下這個函數定義:
virtual HERSULT STDMETHODCALLTYPE GetTypeInfoCount( __RPC__out UINT *pctinfo);
這一篇詳細介紹 HERSULT
在用C++來開發Windows程序時,經常看到下面的判斷情況:
<span style="margin: 0px; padding: 0px; color: rgb(4, 0, 4);">HRESULT hr </span>= ::<span style="margin: 0px; padding: 0px; color: rgb(4, 0, 4);">RegCreateKeyEx</span>(<span style="margin: 0px; padding: 0px; color: rgb(4, 0, 4);">hk</span>, <span style="margin: 0px; padding: 0px; color: rgb(4, 0, 4);">szKeyPath</span>, 0, <span style="margin: 0px; padding: 0px; color: rgb(4, 0, 4);">NULL</span>, <span style="margin: 0px; padding: 0px; color: rgb(4, 0, 4);">REG_OPTION_NON_VOLATILE</span>, <span style="margin: 0px; padding: 0px; color: rgb(4, 0, 4);">KEY_QUERY_VALUE</span>, <span style="margin: 0px; padding: 0px; color: rgb(4, 0, 4);">NULL</span>, &<span style="margin: 0px; padding: 0px; color: rgb(4, 0, 4);">hk</span>, <span style="margin: 0px; padding: 0px; color: rgb(4, 0, 4);">NULL</span>); <span style="margin: 0px; padding: 0px; color: blue;">if </span>(<span style="margin: 0px; padding: 0px; color: rgb(4, 0, 4);">SUCCEEDED</span>(<span style="margin: 0px; padding: 0px; color: rgb(4, 0, 4);">hr</span>)) {
在代碼中,使用SUCCEEDED宏來判斷函數RegCreateKeyEx()函數的返回值。
有些程序員認爲RegCreateKeyEx返回0的時候就是成功,而S_OK就是0,所以就習慣性的用SUCCEEDED宏來做判斷。
還有些人用下面的方法判斷,看起來更嚴謹一些:
<span style="margin: 0px; padding: 0px; color: rgb(4, 0, 4);">HRESULT hr </span>= ::<span style="margin: 0px; padding: 0px; color: rgb(4, 0, 4);">RegCreateKeyEx</span>(<span style="margin: 0px; padding: 0px; color: rgb(4, 0, 4);">hk</span>, <span style="margin: 0px; padding: 0px; color: rgb(4, 0, 4);">szKeyPath</span>, 0, <span style="margin: 0px; padding: 0px; color: rgb(4, 0, 4);">NULL</span>, <span style="margin: 0px; padding: 0px; color: rgb(4, 0, 4);">REG_OPTION_NON_VOLATILE</span>, <span style="margin: 0px; padding: 0px; color: rgb(4, 0, 4);">KEY_QUERY_VALUE</span>, <span style="margin: 0px; padding: 0px; color: rgb(4, 0, 4);">NULL</span>, &<span style="margin: 0px; padding: 0px; color: rgb(4, 0, 4);">hk</span>, <span style="margin: 0px; padding: 0px; color: rgb(4, 0, 4);">NULL</span>); <span style="margin: 0px; padding: 0px; color: blue;">if </span>(<span style="margin: 0px; padding: 0px; color: rgb(4, 0, 4);">S_OK </span>== <span style="margin: 0px; padding: 0px; color: rgb(4, 0, 4);">hr</span>) {
確實,第2種更嚴謹一些,至少不會造成大問題,而第1中則完全是一個大Bug,這個bug在正常情況下是沒有問題的。但一旦有問題,你也發現不了。
錯在哪裏呢?聽我下面來介紹。
SUCCEEDED
先看下這個宏的定義(WinError.h):
<span style="margin: 0px; padding: 0px; color: green;">// // Generic test for success on any status value (non-negative numbers // indicate success). // </span><span style="margin: 0px; padding: 0px; color: blue;">#define </span><span style="margin: 0px; padding: 0px; color: rgb(4, 0, 4);">SUCCEEDED</span>(<span style="margin: 0px; padding: 0px; color: rgb(4, 0, 4);">hr</span>) ((<span style="margin: 0px; padding: 0px; color: rgb(4, 0, 4);">HRESULT</span>)(<span style="margin: 0px; padding: 0px; color: rgb(4, 0, 4);">hr</span>) >= 0)
從這裏可以看出,它就是把hr轉換成HRESULT類型,然後做了下是否大於0的判斷。註釋中也說明:但值爲非負數時表示成功。
也就是說,只要HRESULT是大於等於0的值,它就認爲是成功的。
HRESULT
再來看下HRESULT的定義(winnt.h):
<span style="margin: 0px; padding: 0px; color: green;">// Component Object Model defines, and macros </span><span style="margin: 0px; padding: 0px; color: blue;">#ifndef </span><span style="margin: 0px; padding: 0px; color: rgb(4, 0, 4);">_HRESULT_DEFINED </span><span style="margin: 0px; padding: 0px; color: blue;">#define </span><span style="margin: 0px; padding: 0px; color: rgb(4, 0, 4);">_HRESULT_DEFINED </span><span style="margin: 0px; padding: 0px; color: blue;">typedef </span><span style="margin: 0px; padding: 0px; color: rgb(4, 0, 4);">LONG HRESULT</span>; <span style="margin: 0px; padding: 0px; color: blue;">#endif </span><span style="margin: 0px; padding: 0px; color: green;">// !_HRESULT_DEFINED</span>
哦,原來HRESULT就是一個Long型的整數。
在MSDN中,可以查到更加詳細的資料:
如上圖,HRESULT是一個4字節的Long型,總共32位。其中:
第31位是s位,即符號位,因爲HRESUlT格式規定所有成功都是正的整數,失敗的值都是負數
第30位是r位,是保留位,但n位(28位)沒有設置時,它必須是0;如果n位使用了,則和s位一起來標識NTSTATUS的值。
第29位是c位,表示Custom,即自定義位,如果是微軟定義的返回值,則該位爲0;如果是自定義的,則該位爲1.
第28位是n位,表示NTSTATUS,值爲0的話可以把NTSTATUS值映射爲一個HRESULT值。
第27位是x位,保留位,必須爲0.
第26位到第16位是Facility,用11位來表示錯誤來源,比如
FACILITY_WINDOWS 表示來自Windows子系統
第15位到第1位是Code位,用來保存錯誤值。
從這裏可以看出,只有最後面的2個字節是用來表示返回值的其它的都是輔助信息,它主要用於COM函數的返回值。
常見HRESULT值
Name | Description | Value |
S_OK | 操作成功 | 0x00000000 |
S_FALSE | 操作成功,但是有問題 | 0x00000001L |
E_ABORT | 操作中止 | 0x80004004 |
E_ACCESSDENIED | 拒絕訪問 | 0x80070005 |
E_FAIL | 未知錯誤 | 0x80004005 |
注意:除了S_OK外,還有一個S_FALSE,它也屬於成功。
所以,微軟爲了方便大家使用,專門提供了SUCCEEDED宏和FAILED宏來方便大家做判斷。
到這裏,大家明白了吧:SUCCEEDED宏是用來判斷COM中的函數執行是否成功用的,失敗爲負數,成功爲0和正數。
Windows Error Code
前面的代碼中我們調用了一個Windows API:
:<span style="margin: 0px; padding: 0px; color: rgb(4, 0, 4);">RegCreateKeyEx</span>(<span style="margin: 0px; padding: 0px; color: rgb(4, 0, 4);">hk</span>, <span style="margin: 0px; padding: 0px; color: rgb(4, 0, 4);">szKeyPath</span>, 0, <span style="margin: 0px; padding: 0px; color: rgb(4, 0, 4);">NULL</span>, <span style="margin: 0px; padding: 0px; color: rgb(4, 0, 4);">REG_OPTION_NON_VOLATILE</span>, <span style="margin: 0px; padding: 0px; color: rgb(4, 0, 4);">KEY_QUERY_VALUE</span>, <span style="margin: 0px; padding: 0px; color: rgb(4, 0, 4);">NULL</span>, &<span style="margin: 0px; padding: 0px; color: rgb(4, 0, 4);">hk</span>, <span style="margin: 0px; padding: 0px; color: rgb(4, 0, 4);">NULL</span>);
這個API的聲明是:
LONG WINAPI RegCreateKeyEx( __in HKEY hKey, __in LPCTSTR lpSubKey, __reserved DWORD Reserved, __in_opt LPTSTR lpClass, __in DWORD dwOptions, __in REGSAM samDesired, __in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes, __out PHKEY phkResult, __out_opt LPDWORD lpdwDisposition );
從MSDN中知道,它成功時返回的是ERROR_SUCCESS,其它值則是失敗,其它值就是類似GetLastError的錯誤碼。這些錯誤碼就是Windows Error Code。
Windows Error Codes
微軟在WinError.h定義了大量的Windows Error Codes,這種錯誤碼範圍是0x0000~0xFFFF,即2個字節,但沒限定死2個字節,也可以用4個字節來保存。在Windows API中,大量的使用了這種錯誤碼。比如上面的註冊表API,它的返回值就是這種錯誤碼。
這種錯誤碼還有個特點是微軟爲這些錯誤碼定義了比較詳細的可閱讀的描述信息,它可以通過FormatMessage函數來獲得,在中文環境下,顯示的是翻譯後的中文。
Windows Error Codes 除了ERROR_SUCCESS外,都是正數,也就是不能用SUCCEEDED宏來判斷,因爲這個宏只判斷是不是非負數,對於它而言,所有的Windows Error Codes都是成功的。
常見的Windows Error Codes
Win32 error codes | Description |
0x00000000 ERROR_SUCCESS |
The operation completed successfully. |
0x00000000 NERR_Success |
The operation completed successfully. |
0x00000001 ERROR_INVALID_FUNCTION |
Incorrect function. |
0x00000002 ERROR_FILE_NOT_FOUND |
The system cannot find the file specified. |
0x00000003 ERROR_PATH_NOT_FOUND |
The system cannot find the path specified. |
0x00000004 ERROR_TOO_MANY_OPEN_FILES |
The system cannot open the file. |
0x00000005 ERROR_ACCESS_DENIED |
Access is denied. |
所以前面的代碼中,混淆了HRESULT和Windows Error Code,特別是第一種代碼,當註冊表失敗時它也會判斷爲成功,第2種因爲兩個都是0,碰巧不會出問題,但是建議還是不要這麼混用。
總結