try_catch和__try_except區別


轉自:https://www.cnblogs.com/zhangdongsheng/p/3857509.html

    <div id="post_detail">

窺探try ... catch與__try ... __except的區別

VC中的這兩個東西肯定誰都用過, 不過它們之間有什么區別, 正好有時間研究了一下, 如果有錯誤歡迎拍磚.
基於VC2005, 32位XP 平台測試通過. 估計對於其他版本的VC和操作系統是不通用的.

1. try ... catch

這個是C++語言定義的, 每個C++都有對其的不同的實現. 使用也很簡單. 比如我們有一個函數, 讀入年齡. 如果<=0 或者 >=100, 拋出異常:

int readAge() {
   int age = 讀入年齡;
   if (age <=0 || age >= 100) {
      throw AgeException(age);
   }
   return age;
}

其中 AgeException 的定義為

class AgeException {
   public:
   int errorAge;
   AgeException(int age) {
      errorAge = age;
   }
};

在使用的時候也比較簡單,

try {
   int i = readAge();
   printf("Age inputed is %d", i);
} catch (AgeException e) {
   printf("error. Age inputed = %d and is not valid.", e.errorAge);
}

2. __try ... __except

這個是VC自己定義的不是C++的關鍵字. VC在編譯__try ... __except的時候, 會按照Windows SEH(結構化異常)處理的規則, 把異常處理部分加入到當前線程的異常處理鏈中. 這部分不詳細寫了, SEH處理在網上的文章一搜一大把.

3. try...catch 與 __try ... __except 使用上的區別

對於上面的AgeException, 我們也可以使用__try... __except 來處理:

__try {
   int i = readAge();
   printf("Age inputed is %d", i);
} __except (EXCEPTION_EXECUTE_HANDLER) {
   printf("Age is not correct.");
}

但是, 對於__try ... __except 能夠處理的異常(比如下面的代碼), C++異常處理try .. catch 不能夠捕獲(Catch段不能執行):

try {
   int *p = NULL;
   *p = 0;
} catch (...) {
   printf("Exception occured.");
}

注: 這里其實和編譯器有關, VC2005由/EH加上參數來控制, 詳情參見http://msdn.microsoft.com/en-us/library/1deeycx5(VS.80).aspx . 這里討論的是默認的情況, 不處理的時候.

這是為什么呢. 仔細看了下, 當我們在程序里面throw出來一個異常的時候, 調試器(比如VC, WinDBG)會記錄下面一個事件:

First-chance exception at 0x7c812afb (kernel32.dll) in trycatch.exe: Microsoft C++ exception: AgeException at memory location 0x0012fc98..

也就是說, 在VC中, throw出來的都是Microsoft C++ exception. 只有這種Exception才能被try...catch捕獲. 同樣, 用WinDBG裝載上面的程序

__try {
   int i = readAge();
   printf("Age inputed is %d", i);
} __except (EXCEPTION_EXECUTE_HANDLER) {
   printf("Age is not correct.");
}

會發現, 出現的異常為C++ exception, 異常代碼為0xe06d7363:

也就是說, 在C++中throw出來的異常是一種特殊的類型的異常, 是微軟專門為VC++實現的,異常代碼為0xe06d7363. (有意思的是ASCII碼為0x6d, 0x73, 0x63的字符為msc)

到這里我們基本可以得出一個結論, try...catch和__try...__except其實從本質上來說是一回事, 他們從根源上來說都是用到了Windows的SEH處理機制. 不同點在於:

-) try...catch 只處理異常代碼為0xe06d7363的C++ exception, 不會理會其他的;
-) try...catch 對於編譯器來說做了一些額外的工作, 但是最終的實現是和__try...__except都要歸結於SEH
-) try...catch 多了一些額外的傳遞具體的異常信息的部分(catch的是何種異常. 不像是__try...__except, 需要用ExceptionCode去判斷)

想到這, 想到了下面一個問題, 就是VC++編譯器是如何知道catch的異常信息的呢? 換句話說, 對於下面的代碼, 我們知道出現了異常, 但是怎么得到異常的信息的呢?

__try {
   int i = readAge();
   printf("Age inputed is %d", i);
} __except (EXCEPTION_EXECUTE_HANDLER) {
   printf("Age is not correct."); //如何知道readAge中throw出來的AgeException?
}

為了調試方便, 把異常類和拋出異常的代碼修改一下, 在創建異常的時候傳遞錯誤的age和一條消息

class AgeException {
public:
   int errorAge;
   char *p;
   AgeException(int age, char* msg) {
      errorAge = age;
      p = msg;
   }
};

int readAge() {
   int age = 123; //
   if (age <=0 || age >= 100) {
      throw AgeException(age, "Age is outof range.");
   }
   return age;
}

用WinDBG裝入, 運行, 出現了C++異常后, 使用.exr -1 命令查看最近出現的異常:


可以看出, 這個異常為前面討論的C++異常(0xe06d7363類型), 帶有3個參數, 每個參數, 參數分別為0x19930520, 0x0012fca4, 0x00417bc8. 因為我沒有找到C++異常中參數的含義, 只能猜了(哪位如果知道請賜教).

考慮到拋出異常的代碼拋出的異常類型為AgeException, 那么很自然想到拋出的異常作為一個指針存儲在參數中. 因為沒有資料, 只能挨個試驗了. 使用命令dt trycatch!AgeException 地址, 來把trycatch模塊(編譯出的程序名是trycatch.exe)中地址的內容按照類AgeException顯示出來:



果然, 第一個參數0x19930520里面是不是我們想要的; 當輸入第二個參數的時候, 該地址中的內容和預料的一致, 是我們拋出的異常中的內容. 這樣驗證了猜想. try...catch的工作流程為:

-) 編譯器在編譯try...catch的時候, 也是利用Windows的SEH, 只不過僅僅針對C++異常(0xe06d7363類型)進行處理;
-) 拋出異常的時候(throw), 把生成的異常類的實例地址, 保存在異常信息的第二個參數中
-) catch異常的時候, 從異常信息第2個參數中讀出地址, 並轉化為異常類的實例, 供程序使用.

要使用__try...__except模擬上述的過程, 程序可以改為:

__try {
   int i = readAge();
   printf("Age inputed is %d", i);
} __except (extract(GetExceptionInformation()), EXCEPTION_EXECUTE_HANDLER) {
   printf("Exception happene.");
}

void extract(LPEXCEPTION_POINTERS p) {
   int d = p->ExceptionRecord->NumberParameters; //參數數目, 這里沒用到
   unsigned int * ex = (unsigned int *) p->ExceptionRecord->ExceptionInformation[1]; //第二個參數
   AgeException * e = (AgeException *) ex; //轉換,得到異常類的實例
   printf("==> %d \n", e->errorAge); //異常的信息可以知道了.
    printf("==> %s \n", e->p); //異常的信息可以知道了.
}

運行, 和預期的結果是一致的.

最后可以得到結論(不知道這樣說是否完全正確) :

try...catch是編譯器對__try ... __except的一個包裝; 該包裝僅處理C++異常類型, 但是提供了比較方便的方法來傳遞拋出的異常信息, 這樣程序員能夠比較方便的處理異常, 而不用想上面的例子那樣要手工去異常信息中去取.

本博客注有“轉”字樣的為轉載文章,其余為本人原創文章,轉載請務必注明出處或保存此段。c++/lua/windows逆向交流群:69148232
分類: C++ , Windows
0
0
« 上一篇: 二叉樹與其它樹
» 下一篇: <轉>c++ builder JSONCPP 注意事項 XE2 解決編譯問題 _Mfl
	</div>
	<div class="postDesc">posted @ <span id="post-date">2014-07-20 23:19</span> <a href="https://www.cnblogs.com/zhangdongsheng/">張東升</a> 閱讀(<span id="post_view_count">631</span>) 評論(<span id="post_comment_count">0</span>)  <a href="https://i.cnblogs.com/EditPosts.aspx?postid=3857509" rel="nofollow">編輯</a> <a href="#" onclick="AddToWz(3857509);return false;">收藏</a></div>
</div>
<script type="text/javascript">var allowComments=true,cb_blogId=77777,cb_entryId=3857509,cb_blogApp=currentBlogApp,cb_blogUserGuid='bddefa87-61e0-df11-ac81-842b2b196315',cb_entryCreatedDate='2014/7/20 23:19:00';loadViewCount(cb_entryId);var cb_postType=1;var isMarkdown=false;</script>
</div><!--end: forFlow -->
</div>
posted on 2019-05-16 17:59  泰坦妮克號  閱讀( 1340)  評論( 0編輯  收藏


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM