日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪(fǎng)問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > c/c++ >内容正文

c/c++

VC下发布的Release版程序的异常捕捉

發(fā)布時(shí)間:2025/3/15 c/c++ 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 VC下发布的Release版程序的异常捕捉 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
VC下發(fā)布的Release版程序的異常捕捉
尋找Release版程發(fā)生異常退出的地方比Debug版麻煩得多。發(fā)生異常的時(shí)候windows通常會(huì)彈出一個(gè)錯(cuò)誤對(duì)話(huà)框,點(diǎn)擊詳細(xì)信息,我們能獲得出錯(cuò)的地址和大概的出錯(cuò)信息,然后可以用以下辦法分析我們的程序。
??
一.???? 用MAP文件定位異常代碼位置。
1.???????? 如何生成map文件
打開(kāi)“Project → Project Settings”,選擇 C/C++ 選項(xiàng)卡,在“Debug Info”欄選擇“Line Numbers Only”(或者在最下面的 Project Options 里面輸入:/Zd),然后要選擇 Link 選項(xiàng)卡,選中“Generate mapfile”復(fù)選框,并再次編輯 Project Options,輸入:/mapinfo:lines,以便在 MAP 文件中加入行信息。然后編譯工程則可以在輸出目錄得到同名的.map文件。
2.???????? 使用map文件定位發(fā)生異常的代碼行
編譯得到的map文件可以用文本方式打開(kāi),大致是這樣的格式:(括號(hào)內(nèi)是PomeloWu填加的注釋)
0729???????????????? (←工程名)????
Timestamp is 42e9bc51 (Fri Jul 29 14:19:13 2005)????(←時(shí)間戳)????
Preferred load address is 00400000???????? (←基址)
??……(Data段描述,省略)????
Address???????? Publics by Value??????????????Rva+Base???? Lib:Object?? 0001:00000000?????? ?_GetBaseMessageMap@C0729App@@KGPBUAFX_MSGMAP@@XZ 00401000 f?? 0729.obj……(↑這一行開(kāi)始是函數(shù)信息,下面省略)
Line numbers for ./Release/ShowDlg.obj(C:/0729/ShowDlg.cpp) segment .text??????24 0001:00003f90????28 0001:00003fce????29 0001:00003fd1????30 0001:00003fd4……(行號(hào)信息,前面的數(shù)字是行號(hào),后一個(gè)數(shù)字是偏移量,下面省略)
??在獲得程序異常的地址以后,首先通過(guò)函數(shù)信息部分定位出錯(cuò)的OBJ和函數(shù)。做法是用獲得的異常地址與Rva+Base欄地址進(jìn)行比較(Rva,偏移地址;Base,基址)。找到最后一個(gè)比獲得的異常地址小的那個(gè)函數(shù),那就是出錯(cuò)的函數(shù)。
之后,用獲得的異常地址減去該函數(shù)的Rva+Base,就得到了異常行代碼相對(duì)于函數(shù)起始地址的偏移。在“Line number for”部分找到相對(duì)應(yīng)的模塊,并把其后的行號(hào)信息與上面減得的偏移量對(duì)比,找到最接近的一個(gè),前面的行號(hào)大致就是目標(biāo)行了。
??
二.???? 獲得錯(cuò)誤的詳細(xì)信息。
實(shí)際上,光靠Windows的錯(cuò)誤消息對(duì)話(huà)框提供的信息量是很有限的,用自己寫(xiě)的exception filter可以獲得更多的錯(cuò)誤信息。用SetUnhandledExceptionFilter設(shè)定自定義錯(cuò)誤處理回調(diào)函數(shù)替換Win32默認(rèn)的top-level exception filter:
²???????? SetUnhandledExceptionFilter的函數(shù)原型:
LPTOP_LEVEL_EXCEPTION_FILTER SetUnhandledExceptionFilter(
??LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter
?????????????????????????????????? // exception filter function??
);
²???????? SetUnhandledExceptionFilter返回當(dāng)前的exception filter。應(yīng)當(dāng)保存這個(gè)函數(shù)指針并在不再需要使用自定義錯(cuò)誤處理函數(shù)的時(shí)候當(dāng)作參數(shù)再次調(diào)用SetUnhandledExceptionFilter。
??
²???????? lpTopLevelExceptionFilter 是自定義的exception filter函數(shù)指針,如果傳入NULL值則指定UnhandledExceptionFilter來(lái)負(fù)責(zé)異常處理。lpTopLevelExceptionFilter其函數(shù)原型應(yīng)該是與UnhandledExceptionFilter同型:
LONG WINAPI UnhandledExceptionFilter(
??STRUCT _EXCEPTION_POINTERS *ExceptionInfo?? // address of
??????????????????????????????????????????????// exception info
);
²???????? lpTopLevelExceptionFilter的返回值應(yīng)該是下面3種之一:
EXCEPTION_EXECUTE_HANDLER = 1
EXCEPTION_CONTINUE_EXECUTION = -1
這兩個(gè)返回值都應(yīng)該由調(diào)用UnhandledExceptionFilter后返回。
EXCEPTION_EXECUTE_HANDLER 表示進(jìn)程結(jié)束
EXCEPTION_CONTINUE_EXECUTION表示處理異常之后繼續(xù)執(zhí)行
EXCEPTION_CONTINUE_SEARCH = 0
進(jìn)行系統(tǒng)通常的異常處理(錯(cuò)誤消息對(duì)話(huà)框)
??
²???????? lpTopLevelExceptionFilter的唯一的參數(shù)是_EXCEPTION_POINTERS結(jié)構(gòu)指針。
typedef struct _EXCEPTION_POINTERS { // exp
????PEXCEPTION_RECORD ExceptionRecord;
????PCONTEXT ContextRecord;
} EXCEPTION_POINTERS;
其中PCONTEXT是一個(gè)指向進(jìn)程上下文結(jié)構(gòu)的指針,保存了各個(gè)寄存器在異常發(fā)生的時(shí)候的值,詳細(xì)信息參考《Windows核心編程》。
ExceptionRecord則指向另一個(gè)結(jié)構(gòu)體EXCEPTION_RECORD:
typedef struct _EXCEPTION_RECORD { // exr
????DWORD ExceptionCode;
????DWORD ExceptionFlags;
????struct _EXCEPTION_RECORD *ExceptionRecord;
????PVOID ExceptionAddress;
????DWORD NumberParameters;
????DWORD ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
} EXCEPTION_RECORD;
DWORD ExceptionCode;
異常代碼,指出異常原因。常見(jiàn)異常代碼有:
EXCEPTION_ACCESS_VIOLATION = C0000005h
讀寫(xiě)內(nèi)存沖突
EXCEPTION_INT_DIVIDE_BY_ZERO = C0000094h
除0錯(cuò)誤
EXCEPTION_STACK_OVERFLOW = C00000FDh
堆棧溢出或者越界
EXCEPTION_GUARD_PAGE = 80000001h
由Virtual Alloc建立起來(lái)的屬性頁(yè)沖突
EXCEPTION_NONCONTINUABLE_EXCEPTION = C0000025h
不可持續(xù)異常,程序無(wú)法恢復(fù)執(zhí)行,異常處理例程不應(yīng)處理這個(gè)異常
EXCEPTION_INVALID_DISPOSITION = C0000026h
在異常處理過(guò)程中系統(tǒng)使用的代碼
EXCEPTION_BREAKPOINT = 80000003h
調(diào)試時(shí)中斷(INT 3)
EXCEPTION_SINGLE_STEP = 80000004h
單步調(diào)試狀態(tài)(INT 1)
DWORD ExceptionFlags;
異常標(biāo)志
0,表示可修復(fù)異常
EXCEPTION_NONCONTINUABLE = 1,表示不可修復(fù)異常。在不可修復(fù)異常后嘗試?yán)^續(xù)執(zhí)行會(huì)導(dǎo)致EXCEPTION_NONCONTINUABLE_EXCEPTION = C0000025H異常。
struct _EXCEPTION_RECORD *ExceptionRecord;
當(dāng)異常處理程序中發(fā)生異常時(shí),此字段被填充,否則為NULL
PVOID ExceptionAddress;
發(fā)生異常的地址(EIP)
DWORD NumberParameters;
規(guī)定與異常相關(guān)的參數(shù)數(shù)量(0-15),是ExceptionInformation數(shù)組中元素個(gè)數(shù)。
DWORD ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
異常描述信息,大多數(shù)異常都未定義此數(shù)組,僅有EXCEPTION_ACCESS_VIOLATION 異常的描述信息:
ExceptionInformation[0],描述導(dǎo)致異常的操作類(lèi)型
= 0 讀異常
= 1 寫(xiě)異常
ExceptionInformation[1],發(fā)生讀寫(xiě)異常的內(nèi)存地址??
也就是說(shuō),只要注冊(cè)了自己寫(xiě)的這個(gè)exception filter,一旦發(fā)生異常,進(jìn)入這個(gè)exception filter,從參數(shù)我們就能獲得各種需要的信息了。而這個(gè)exception filter需要做的就是保存這些信息,然后將異常處理的事情交還給系統(tǒng)就行了:
// in the beginning
// Install the unhandled exception filter function
g_previousFilter = SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);
??
// exception filter
LONG WINAPI MyUnhandledExceptionFilter(PEXCEPTION_POINTERS pExceptionInfo)
{
????WriteLogFile(pExceptionInfo);??// 寫(xiě)入文件
????if ( g_previousFilter )
?????????? return g_previousFilter( pExceptionInfo );
????????else
????????????return EXCEPTION_CONTINUE_SEARCH;
????}
??
三.???? 使用COD文件精確分析異常原因
說(shuō)精確分析多少有點(diǎn)言過(guò)其實(shí),發(fā)生異常的情況各不相同,分析真正原因很可能是一件極其復(fù)雜的事情。不過(guò)用COD文件能比MAP文件更精確地定位產(chǎn)生異常的位置。結(jié)合匯編代碼和自定義的exception filter獲得的錯(cuò)誤情報(bào)寄存器狀態(tài)等各種信息,找到異常發(fā)生的直接原因是很容易的。
1.???????? 如何生成cod文件
仍然是打開(kāi)“Project → Project Settings”,選擇 C/C++ 選項(xiàng)卡,在“Category”欄選擇“Listing Files”然后在Listing file type欄選擇“Assembly with Machine Code”。重新編譯工程后則可以在輸出目錄看到與每一個(gè).cpp文件同名的.cod文件。
2.???????? Cod文件的使用
首先還是利用map文件用獲得的程序異常地址通過(guò)函數(shù)信息部分定位出錯(cuò)的OBJ和函數(shù),并同樣記錄偏移地址(用獲得的異常地址減去該函數(shù)的Rva+Base的差值)。然后,在相應(yīng)的cod文件中(而不是在map文件后面的行號(hào)信息部分)來(lái)查找出錯(cuò)的函數(shù),找到如下的格式:
?OnPaint@CShowDlg @@IAEXXZ PROC NEAR ; CShowDlg::OnPaint, COMDAT?? ; 81?? : { ??(←格式為: 行號(hào) : 源代碼)???? 00000 83 ec 64 sub esp, 100 ; 00000064H (↑偏移地址)??(↖機(jī)器碼)?? (↑匯編碼)?? 00003 56 push esi?? ; 82?? : if (IsIconic()) (下面省略)
?? 找到出錯(cuò)的函數(shù)以后,再用偏移地址就能找到準(zhǔn)確的異常發(fā)生的地方。然后通過(guò)源程序、匯編碼即可進(jìn)行更詳盡的分析了。

總結(jié)

以上是生活随笔為你收集整理的VC下发布的Release版程序的异常捕捉的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。