此異常非彼異常(續2)

來源:互聯網
上載者:User

轉載請註明出處

作者:小馬

剛開始準備寫關於異常的文章時,也沒想到會一連寫三篇.

本篇就是接著寫上篇寫的, 關於如何把windows的SE異常與c++的try/catch整合在一起.

要實現整合, 要用_set_se_translator, 我並不是簡單的直接用這個函數, 因為畢竟c++是一種物件導向語言, _set_se_translator是一個標準的c函數, 為了不破壞物件導向的思想, 我寫了一類把_set_se_translator封裝起來, 然後我希望在實際的開發中, 可以類似這樣應用(我把這個類的命名為CPony_SeException):

try { //各種處理......} catch(CPony_SeException &e1){ cout << "windows se exception happened" <<endl;}catch(runtime_error &e2) {  cout << "c++ run-time error happened" <<endl;}catch(...){ cout << "other c++ exception happened" <<endl;}

 

先簡單說一下_set_se_translator的原理.

我們需要自己寫一個函數, 這個函數的參數和傳回值都必須符合下面的形式:
typedef void (*_se_translator_function)(unsigned int, struct _EXCEPTION_POINTERS* );
然後我們把這個函數作為參數, 傳給_set_se_translator.這個過程可以認為是用_set_se_translator註冊這個函數.

_set_se_translator是一個CRT,跟系統無關. 它提供了一種機制,可以把windows的seh異常轉化為標準的c++異常(try/catch), 過程如下:當一個se異常產生時, 它會調用我們寫的那個函數,而這個函數一般只做一個操作, 就是throw一個c++的異常.

另外, 補充兩點,
1 _set_se_translator是基於線程的, 對於多線程的應用,每一個線程都要轉換一次.
2 用_set_se_translator必須開啟/eha(前面文章介紹過如何在vs2005中開啟這個開關)

 

我的類來如下:

class CPony_SeException :public exception{public: CPony_SeException(CPony_SeException const& copee); CPony_SeException(unsigned int exceptionID, EXCEPTION_POINTERS* exceptionPointers); virtual ~CPony_SeException(); unsigned int GetExceptionID() const; EXCEPTION_POINTERS* GetExceptionPointers() const; virtual char const * what() const;private:  static void sehTranslator(unsigned int exceptionID, EXCEPTION_POINTERS* exceptionPointers); unsigned int m_exceptionID; EXCEPTION_POINTERS* m_exceptionPointers; static _se_translator_function m_oldSEHTranslator;};

 

比較簡單的一個類, 解釋幾個地方:
1 exception是c++的異常基類, what是它的一個虛函數, c++中所有的異常類型都是派生自這個類, 更多詳細的東西,可以看<<c++ primer>>.

2 EXCEPTION_POINTERS裡記錄了SE異常的詳細內容, 比如異常發生的地址, 異常類型等, 我對外引出一個介面(GetExceptionPointers), 這樣在異常發生時, 可以擷取自己想要的資訊.

3 GetExceptionID可以返回異常類型碼, windows定義了以下幾種:

#define WAIT_IO_COMPLETION                  STATUS_USER_APC#define STILL_ACTIVE                        STATUS_PENDING#define EXCEPTION_ACCESS_VIOLATION          STATUS_ACCESS_VIOLATION#define EXCEPTION_DATATYPE_MISALIGNMENT     STATUS_DATATYPE_MISALIGNMENT#define EXCEPTION_BREAKPOINT                STATUS_BREAKPOINT#define EXCEPTION_SINGLE_STEP               STATUS_SINGLE_STEP#define EXCEPTION_ARRAY_BOUNDS_EXCEEDED     STATUS_ARRAY_BOUNDS_EXCEEDED#define EXCEPTION_FLT_DENORMAL_OPERAND      STATUS_FLOAT_DENORMAL_OPERAND#define EXCEPTION_FLT_DIVIDE_BY_ZERO        STATUS_FLOAT_DIVIDE_BY_ZERO#define EXCEPTION_FLT_INEXACT_RESULT        STATUS_FLOAT_INEXACT_RESULT#define EXCEPTION_FLT_INVALID_OPERATION     STATUS_FLOAT_INVALID_OPERATION#define EXCEPTION_FLT_OVERFLOW              STATUS_FLOAT_OVERFLOW#define EXCEPTION_FLT_STACK_CHECK           STATUS_FLOAT_STACK_CHECK#define EXCEPTION_FLT_UNDERFLOW             STATUS_FLOAT_UNDERFLOW#define EXCEPTION_INT_DIVIDE_BY_ZERO        STATUS_INTEGER_DIVIDE_BY_ZERO#define EXCEPTION_INT_OVERFLOW              STATUS_INTEGER_OVERFLOW#define EXCEPTION_PRIV_INSTRUCTION          STATUS_PRIVILEGED_INSTRUCTION#define EXCEPTION_IN_PAGE_ERROR             STATUS_IN_PAGE_ERROR#define EXCEPTION_ILLEGAL_INSTRUCTION       STATUS_ILLEGAL_INSTRUCTION#define EXCEPTION_NONCONTINUABLE_EXCEPTION  STATUS_NONCONTINUABLE_EXCEPTION#define EXCEPTION_STACK_OVERFLOW            STATUS_STACK_OVERFLOW#define EXCEPTION_INVALID_DISPOSITION       STATUS_INVALID_DISPOSITION#define EXCEPTION_GUARD_PAGE                STATUS_GUARD_PAGE_VIOLATION#define EXCEPTION_INVALID_HANDLE            STATUS_INVALID_HANDLE#define EXCEPTION_POSSIBLE_DEADLOCK         STATUS_POSSIBLE_DEADLOCK

 

4 sehTranslator就是我們要自己實現的異常轉換函式,注意這裡把它定義成static,因為它是基於類的, 而不是某個對象.

5 m_oldSEHTranslator是一個函數指標.

 

下面到.cpp檔案, 是類CPony_SeException的實現

 

#define CASE(nSeCode,szDescription) case nSeCode: \ sprintf(szDescription, "%s", #nSeCode); \ break;

這裡定義一個宏, 把整數型別碼轉換成字串, 下面的what函數會用到, 方便輸出.

 

_se_translator_function CPony_SeException::m_oldSEHTranslator = _set_se_translator(CPony_SeException::sehTranslator);

_set_se_translator本身有個傳回值, 是上一個被它註冊的函數指標, 這裡儲存這個指標,以防會用到. 其實這裡面還有一個重要的學問. 仔細看一下, 這一句我是在.cpp檔案裡調用的, sehTranslator是私人函數, 我用_set_se_translator直接存取, 為什麼沒有報錯呢? 原因就是等號左邊起了作用, <<c++ primer>>有說, 一旦成員名出現, static的定義就在類的範圍裡了. 所以我可以訪問它的私人成員. 可以試一下, 如果把=號左邊的去掉,直接寫:
_set_se_translator(CPony_SeException::sehTranslator);
程式會報錯的.

 

CPony_SeException::CPony_SeException(CPony_SeException const& exceptionCopee):m_exceptionID(exceptionCopee.m_exceptionID), m_exceptionPointers(exceptionCopee.m_exceptionPointers){}CPony_SeException::CPony_SeException(unsigned int exceptionID, EXCEPTION_POINTERS* exceptionPointers): m_exceptionID(exceptionID), m_exceptionPointers(exceptionPointers){}CPony_SeException::~CPony_SeException(){}unsigned int CPony_SeException::GetExceptionID() const{ return m_exceptionID;}EXCEPTION_POINTERS* CPony_SeException::GetExceptionPointers() const{ return m_exceptionPointers;}

上面這些沒什麼好解釋的.

 

char const * CPony_SeException::what() const{ char strError[100] = {0}; static char strWhat[200] = {0}; strcat(strWhat, "windows SE exception: "); switch (m_exceptionID) {  CASE(EXCEPTION_ACCESS_VIOLATION,strError);  CASE(EXCEPTION_DATATYPE_MISALIGNMENT,strError);  CASE(EXCEPTION_BREAKPOINT,strError);  CASE(EXCEPTION_SINGLE_STEP,strError);  CASE(EXCEPTION_ARRAY_BOUNDS_EXCEEDED,strError);  CASE(EXCEPTION_FLT_DENORMAL_OPERAND,strError);  CASE(EXCEPTION_FLT_DIVIDE_BY_ZERO,strError);  CASE(EXCEPTION_FLT_INEXACT_RESULT,strError);  CASE(EXCEPTION_FLT_INVALID_OPERATION,strError);  CASE(EXCEPTION_FLT_OVERFLOW,strError);  CASE(EXCEPTION_FLT_STACK_CHECK,strError);  CASE(EXCEPTION_FLT_UNDERFLOW,strError);  CASE(EXCEPTION_INT_DIVIDE_BY_ZERO,strError);  CASE(EXCEPTION_INT_OVERFLOW,strError);  CASE(EXCEPTION_PRIV_INSTRUCTION,strError);  CASE(EXCEPTION_IN_PAGE_ERROR,strError);  CASE(EXCEPTION_ILLEGAL_INSTRUCTION,strError);  CASE(EXCEPTION_NONCONTINUABLE_EXCEPTION,strError);  CASE(EXCEPTION_STACK_OVERFLOW,strError);  CASE(EXCEPTION_INVALID_DISPOSITION,strError);  CASE(EXCEPTION_GUARD_PAGE,strError);  CASE(EXCEPTION_INVALID_HANDLE,strError); default:  strcpy(strError, "Unknown exception.");  break; } strcat(strWhat, strError); return strWhat;}

重定義的what函數, 用到了上面的宏, 這樣可以以字串的形式返回SE的異常類型碼, 非常方便.

 

void CPony_SeException::sehTranslator(unsigned int exceptionID,          EXCEPTION_POINTERS* exceptionPointers){ throw CPony_SeException(exceptionID, exceptionPointers);}

這個就是異常轉換函式, 它最好除了throw一個異常外, 什麼也不要做.

 

好了, 類寫完了, 我可以在我的main函數裡這樣用:

int main(){int *p = NULL;try  {  *p = 0;//非法記憶體訪問 }  //避免類對象的複製,這裡用引用.  catch(CPony_SeException &e)  {  cout << e.what() <<endl; }  return 0;}

 

輸出:
windows SE exception: EXCEPTION_ACCESS_VIOLATION

注意:一般要保證CPony_SeException類本身不能拋出SE異常, 如果出現了,肯定無法被它自己捕捉,也是會報系統錯誤的.

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.