要想學(xué)習(xí)好VC必須具備良好的C/C++的基礎(chǔ),必要的英語閱讀能力也是必不可少的,因為大量的技術(shù)文檔多以英文形式發(fā)布,否則就會導(dǎo)致VC++編譯異常,這大大的影響了程序員的效率。
代碼
struct EXCEPTION_REGISTRATION
{
EXCEPTION_REGISTRATION* prev;
DWORD handler;
int id;
DWORD ebp;
};
VC++編譯異常會為絕大部分函數(shù)③添加一個EXCEPTION_REGISTRATION類型的局部變量,它的最后一個字段(ebp)與棧楨指針指向的位置重疊。函 數(shù)的序言創(chuàng)建這個結(jié)構(gòu)并把它注冊給操作系統(tǒng),尾聲則恢復(fù)主調(diào)函數(shù)的EXCEPTION_REGISTRATION。id字段的意義我將在下一節(jié)介紹。
VC++編譯函數(shù)時會為它生成兩部分?jǐn)?shù)據(jù)
a)異常回調(diào)函數(shù)
b)一個包含函數(shù)重要信息的數(shù)據(jù)結(jié)構(gòu),這些信息包括catch塊、這些塊的地址和這些塊所關(guān)心的異常的類型等等。我把這個結(jié)構(gòu)稱為funcinfo,有關(guān)它的詳細(xì)討論也在下一節(jié)。
是考慮了異常處理之后的運行時堆棧。widget的異常回調(diào)函數(shù)位于由FS:[0]指向的異常處理鏈的開始位置(這是由widget的序言設(shè)置的)。
異常處理
異常處理程序把widget的funcinfo結(jié)構(gòu)的地址交給函數(shù)__CxxFrameHandler,__CxxFrameHandler會檢查這個結(jié) 構(gòu)看函數(shù)中有沒有catch塊對當(dāng)前的異常感興趣。
如果沒有的話,它就返回ExceptionContinueSearch給操作系統(tǒng),于是操作系統(tǒng)會從 異常處理鏈表中取得下一個結(jié)點,并調(diào)用它的異常處理程序(也就是調(diào)用當(dāng)前函數(shù)的那個函數(shù)的異常處理程序)。
這一過程將一直進(jìn)行下去——直到處理程序找到一個能處理當(dāng)前異常的catch塊為止,這時它就不再返回操作系統(tǒng)了。但是在調(diào)用catch塊之前(由于有 funcinfo結(jié)構(gòu),所以知道catch塊的入口,參見圖3),必須進(jìn)行堆棧展開,也就是清理掉當(dāng)前函數(shù)的棧楨下面的所有其他的棧楨。這個操作稍微有點 復(fù)雜。
因為:異常處理程序必須找到異常發(fā)生時生存在這些棧楨上的所有局部對象,VC++編譯異常并依次調(diào)用它們的析構(gòu)函數(shù)。后面我將對此進(jìn)行詳細(xì)介紹。 異常處理程序把這項工作委托給了各個棧楨自己的異常處理程序。從FS:[0]指向的異常處理鏈的第一個結(jié)點開始,它依次調(diào)用每個結(jié)點的處理程序,告訴它堆 棧正在展開。
與之相呼應(yīng),這些處理程序會調(diào)用每個局部對象的析構(gòu)函數(shù),然后返回。此過程一直進(jìn)行到與異常處理程序自身相對應(yīng)的那個結(jié)點為止。 由于catch塊是函數(shù)的一部分,所以它使用的也是函數(shù)的棧楨。因此,在調(diào)用catch塊之前,異常處理程序必須激活它所隸屬的函數(shù)的棧楨。
其次,每個catch塊都只接受一個參數(shù),VC++編譯異常其類型是它希望捕獲的異常的類型。異常處理程序必須把異常對象本身或者是異常對象的引用拷貝到catch塊的棧 楨上,編譯器在funcinfo中記錄了相關(guān)信息,處理程序根據(jù)這些信息就能知道到哪去拷貝異常對象了。