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