Windows核心編程之核心總結(第一章 錯誤處理)(2018.5.26)

來源:互聯網
上載者:User

標籤:Windows核心編程之核心總結

前沿

學習Windows核心編程是步入Windows編程殿堂的必經之路,2018年寒假重溫了電腦作業系統知識,前陣子又過學習Windows程式設計方面的基礎,正所謂打鐵要乘熱,所以我又入了Windows核心編程的坑啦,哈哈~

學習目標

每一章的學習都要明確一個目標,就是你學完這一章之後你能做些什嗎?好的,我們一步步來學習第一章節錯誤處理。以下是這一章節的學習目標:
1.瞭解Windows函數的錯誤機制
2.瞭解GetLastError和SetLastError函數的使用
3.瞭解FormatMessage函數使用及參數說明
4.通過以上的學習,自行寫出將錯誤碼轉換為錯誤資訊代碼例子。
5.結合Windows核心編程給出的ErrorShow樣本程式,分析第4點自己寫的代碼。

Windows函數的錯誤機制

Windows核心編程這本書的第一章是最簡單的,雖然簡單,但我們不能驕傲,因 為我們要征服這本書就要以虛心向學的態度學習和總結,再應用。
我們都知道調用Windows函數時,它會先驗證我們傳給它的參數,驗證通過後再開始執行任務。如果傳入的參數無效或者由於其他原因導致操作無法執行,則函數的傳回值將指出函數因為某些原因失敗了。例如:最經典的函數莫過於CreateFile函數,假如開啟檔案失敗,那麼函數的傳回值會是INVALID_HANDLE_VALUE(-1)。

HANDLE hFile=CreateFile(TEXT("c:\\fish"),0,0,NULL,OPEN_EXISTING,0,NULL);

下面展示大多數Windows函數使用的傳回值的資料類型:

> VOID:這個函數不可能失敗!> BOOL:FALSE失敗;TRUE成功。> HANDLE:失敗返回NULL,否則返回非零控制代碼。如果有特殊說明,則可能為特殊值例如:INVALID_HANDLE_VALUE。> PVOID:返回一個記憶體位址,失敗為NULL> LONG/DWORD:應該根據SDK說明來確定函數狀況。

可以發現,雖然函數的傳回值能夠告訴我們函數的執行失敗,但是卻沒有告訴我們函數為什麼會調用失敗。所以,Microsoft編輯了一個列表,其中列出了所有可能的錯誤碼,並為每個錯誤碼都分配了一個32位的編號,其實錯誤碼是一個DWORD(unsigned long)類型的值。然後每個錯誤碼都定義了一個錯誤資訊,這個錯誤清單是儲存在WinError.h標頭檔中。下面截個WinError.h標頭檔的頭部分:

#define ERROR_SUCCESS                    0L#define NO_ERROR 0L                                                 // dderror#define SEC_E_OK                         ((HRESULT)0x00000000L)//// MessageId: ERROR_INVALID_FUNCTION//// MessageText://// Incorrect function.//#define ERROR_INVALID_FUNCTION           1L    // dderror//// MessageId: ERROR_FILE_NOT_FOUND//// MessageText://// The system cannot find the file specified.//#define ERROR_FILE_NOT_FOUND             2L//// MessageId: ERROR_PATH_NOT_FOUND//// MessageText://// The system cannot find the path specified.//#define ERROR_PATH_NOT_FOUND             3L//// MessageId: ERROR_TOO_MANY_OPEN_FILES//// MessageText://// The system cannot open the file.//#define ERROR_TOO_MANY_OPEN_FILES        4L
GetLastError和SetLastError函數

好了,到了這裡我們對Windows函數的錯誤機制有了一定的瞭解,那麼我們程式中就要想辦法擷取錯誤碼和設定錯誤碼。
GetLastError函數擷取調用線程的上一次錯誤碼。
函數原型:DWORD WINAPI GetLastError(void);
傳回值:返回該線程的上一次錯誤碼。錯誤碼是一個32位無符號長整型(DWORD).
備忘:Windows函數失敗之後,應該馬上調用GetLastError,因為有可能下一次的函數調用影響這次的錯誤碼值。
SetLastError函數用於為調用線程設定最近的錯誤碼。
函數原型:void WINAPI SetLastError(In DWORD dwErrCode);
參數:DWORD類型的錯誤碼。
備忘:利用GetLastError函數返回線程的上一個錯誤碼,而通過這SetLastError函數設定錯誤碼。

(1)對於上面GetLastError和SetLastError函數的使用方法後面我會舉例。現在,介紹一個Visual Studio的Watch視窗,以前學習C語言和C++我很少使用這個功能,一般都是看局部變數視窗,然後調試進行下一過程來查看各變數的變化情況。現在舉個例子來說明Visual Studio的Watch視窗的使用方法。圖片中監視1視窗的左側名稱是我自己輸入進去的,例如:count、hmodule、$err,hr。

(2)我們可以看到我將斷點放在定義count的時候,當我輸入1000時就進入調試階段,其實WinError.h標頭檔並沒有1000這個代碼值的定義,那麼這個FormatMessage函數的調用就會報錯。然後點擊逐過程進入下一條語句。

(3)可以發現count變數成功被初始化成0了。而hmodule由於是條件陳述式內部還未執行到那,所以顯示未定義。這時候我再點擊逐過程進入下一條語句,這時候FormatMessage函數已經執行完成了,看下$err.hr的值變為錯誤碼加上錯誤碼描述資訊。可以總結$err是顯示函數調用失敗的錯誤碼,而hr是錯誤碼的描述資訊,你也可以唯寫$err,那麼就不是錯誤碼資訊了,只有錯誤碼。

(4)假如我們寫一個函數給其他人調用,這個函數有可能會出錯,那麼我們需要向調用者指出錯誤,那麼我們就可以自己在函數中調用SetLastError函數,然後我們的函數返回FALSE或者NULL或者其他合適的值。注意,SetLastError參數是一個DWORD類型值。而錯誤碼由幾個欄位組成,下面貼圖:

FormatMessage函數

下面是我對 FormatMessage函數的各參數一部分總結:

FormatMessage函數就是將GetLastError函數得到的錯誤資訊(這個錯誤資訊是數字代號)轉化成字串資訊的函數。

函數原型:

DWORD WINAPI FormatMessage(
In DWORD dwFlags,
_Inopt LPCVOID lpSource,
In DWORD dwMessageId,
In DWORD dwLanguageId,
Out LPTSTR lpBuffer,
In DWORD nSize,
_Inopt va_list *Arguments
);

參數1:標誌位
FORMAT_MESSAGE_ALLOCATE_BUFFER 0x00000100
如果設定了這個標誌位,那麼參數lpBuffer(經過初始化的指標變數)必須按(LPTSTR)&lpBuffer形式作為參數來進行函數調用,當函數執行成功後,函數會自動分配一塊記憶體塊,該記憶體塊含有我們想要的字串,然後這個指標會指向該記憶體塊,那麼我們就可以引用該字串了。

FORMAT_MESSAGE_ARGUMENT_ARRAY 0x00002000
這個標誌位代表參數Arguments只是一個數組,不是va_list這種類型的參數。

FORMAT_MESSAGE_FROM_HMODULE 0x00000800
這個標誌說明,這個函數接收一個DLL模組,從DLL模組中尋找字串。

FORMAT_MESSAGE_FROM_STRING 0x00000400
從一個字串中,尋找訊息字串。

FORMAT_MESSAGE_FROM_SYSTEM 0x00001000
從系統中擷取訊息字串,不是從某個指定的字串或者DLL中擷取訊息字串。

FORMAT_MESSAGE_IGNORE_INSERTS 0x00000200
這個表示說明Argument是否有用,如果設定了這個標誌,那麼Argument就會被函數忽略,如果沒有設定這個標誌位,那麼就必須在*Argument參數中提供這些預留位置的值。

參數2:lpSource:
從哪裡擷取訊息字串?如果是從系統擷取訊息字串的話,這個參數為NULL。
參數3:dwMessageId:
訊息索引
參數4:dwLanguageId:
訊息的語言種類。
參數5:lpBuffer:
接受訊息的記憶體塊指標。
參數6:nSize:
接受訊息的記憶體塊大小,以位元組為大小。
參數7:*Arguments:
訊息中有些變數的值。
傳回值:如果函數調用成功,返回字元訊息的字元數。否則返回0.

自己編寫的代碼例子(附上注釋說明)

我這個例子使用MFC簡單對話方塊,利用FormatMessage函數實現將錯誤碼轉化為錯誤碼資訊,並將錯誤碼資訊黏貼在靜態文本控制項。下面是Ok按鈕的點擊事件處理,重要的還是這個內部代碼的實現,對於MFC不作過多講解,其實我對MFC也只是小白+1的存在,我主要學習Qt介面的開發。

void CMFCApplication1Dlg::OnBnClickedOk(){    // TODO:  在此添加控制項通知處理常式代碼    TCHAR *str=NULL;//訊息字串緩衝區    this->UpdateData(1);    /*    FormatMessage函數進行參數的設定後,作用是為這個函數傳入錯誤碼,該函數就會返回對應系統或者DLL檔案或者字串中錯誤碼資訊字串。    該函數的傳回值是一個DWORD類型的值,如果函數調用成功則返回字串的字元數目,若失敗則返回0.    所以這裡定義了一個count儲存FormatMessage函數的傳回值。    */    DWORD count = 0;    count=FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER,        NULL, m_value, NULL, (LPTSTR)&str, 0, NULL);    //函數調用成功    if (count)    {        this->SetDlgItemTextW(IDC_STATIC1, (LPCTSTR)LocalLock(str));//在靜態文本控制項的文本設定為所擷取的錯誤碼資訊        LocalFree(str);//如果FormatMessage函數是通過函數自動分配記憶體塊來儲存錯誤碼資訊,那麼就要調用LocalFree函數釋放掉這個記憶體。    }    //函數調用失敗,因為上方調用失敗的原因可能是輸入的錯誤碼在系統定義中沒有,那麼就尋找網路錯誤碼資訊,這裡載入網路錯誤碼資訊的DLL檔案    else    {        //載入網路錯誤碼DLL檔案,名字要記住        HMODULE hmodule = LoadLibrary(TEXT("netmsg.dll"));        //載入成功        if (hmodule)        {            //再次調用FormatMessage函數,但參數1的標誌不同,第一個標誌變為FORMAT_MESSAGE_FROM_HMODULE            count = FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER,                hmodule, m_value, NULL, (LPTSTR)&str, 0, NULL);            //函數調用成功            if (count)            {                //在靜態文本控制項的文本設定為所擷取的錯誤碼資訊                this->SetDlgItemTextW(IDC_STATIC1, (LPCTSTR)LocalLock(str));                LocalFree(str);//釋放函數自動分配的記憶體塊            }            FreeLibrary(hmodule);//釋放動態連結程式庫檔案的記憶體塊        }    }    //如果上面通過兩種不同源路徑都擷取不到錯誤碼對應的資訊,那麼就輸出一行報錯文字    if (count == 0)    {        this->SetDlgItemTextW(IDC_STATIC1, (LPCTSTR)L"沒有找到錯誤碼資訊");    }}
ErrorShow樣本程式
DWORD dwError = GetDlgItemInt(hwnd, IDC_ERRORCODE, NULL, FALSE);//擷取輸入框的值,即錯誤碼值      HLOCAL hlocal = NULL;   // Buffer that gets the error message string      //HLOCAL=HANDLE=void *,這三個都是一樣的      // Use the default system locale since we look for Windows messages.      // Note: this MAKELANGID combination has 0 as value      DWORD systemLocale = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);//語言      // Get the error code‘s textual description      BOOL fOk = FormatMessage(         FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS |         FORMAT_MESSAGE_ALLOCATE_BUFFER,          NULL, dwError, systemLocale,          (PTSTR) &hlocal, 0, NULL);                 //如果函數調用失敗則採取另一種方法來擷取,即動態連結程式庫檔案      if (!fOk) {         // Is it a network-related error?         HMODULE hDll = LoadLibraryEx(TEXT("netmsg.dll"), NULL,             DONT_RESOLVE_DLL_REFERENCES);         if (hDll != NULL) {            fOk = FormatMessage(               FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS |               FORMAT_MESSAGE_ALLOCATE_BUFFER,               hDll, dwError, systemLocale,               (PTSTR) &hlocal, 0, NULL);            FreeLibrary(hDll);         }      }      if (fOk && (hlocal != NULL)) {         SetDlgItemText(hwnd, IDC_ERRORTEXT, (PCTSTR) LocalLock(hlocal));         LocalFree(hlocal);      } else {         SetDlgItemText(hwnd, IDC_ERRORTEXT,             TEXT("No text found for this error number."));      }
總結

經過以上的學習,Windows核心編程的第一章就講完了,也就那麼多。我們分析自己寫的例子和ErrorShow樣本程式之間的區別其實不是很大,但FormatMessage樣本程式的邏輯更加嚴謹。還有,如果也有童鞋正在學習Windows核心編程,我們可以相互交流學習,本人不才但好學研究,還需要不斷學習來提升自己。可以通過QQ來聯絡我,我們一起不斷進步!
QQ:764238383

Windows核心編程之核心總結(第一章 錯誤處理)(2018.5.26)

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.