第25章 SEH结构化异常处理_未处理异常及向量化异常
25.1 UnhandledExceptionFilter函数详解
25.1.1 BaseProcessStart伪代码(Kernel32内部)
- void BaseProcessStart(PVOID lpfnEntryPoint) //参数为线程函数的入口地址
- {
- DWORD retValue;
- DWORD currentESP;
- DWORD exceptionCode;
- currentESP = ESP;
- //lpfnEntryPoint被try/except封装着,这是系统安装的默认的异常处理程序,也是SEH链上最后一个异常处理程序
- __try
- {
- NtSetInformationThread(GetCurrentThread(),
- ThreadQuerySetWin32StartAddress,
- &lpfnEntryPoint,
- sizeof(lpfnEntryPoint));
- retValue = lpfnEntryPoint();
- ExitThread(retValue); //如果异常,线程从这里退出!
- }
- __except ( //过滤器表达式代码
- exceptionCode = GetExceptionInformation(),
- UnhandledExceptionFilter(GetExceptionInformation())) //出现异常会调用Unhandled...这个函数,该函数内部会调用
//用户通过SetUnhandledFilter设置的全局异常处理函数。- {
- //如果UnhandledExceptionFilter返回EXCEPTION_EXECUTE_HANDLER,则会控制流会执行到这里
- ESP = currentESP;
- if (!_BaseRunningInServerProcess) //普通进程,则退出进程
- ExitProcess(exceptionCode);
- else // 线程是作为服务来运行的,只退出线程并不终止整个服务
- ExitThread(exceptionCode);
- }
- }
(1)如果异常过滤程序返回EXCEPTION_CONTINUE_SEARCH时,系统会继续向外层寻找异常过滤程序。但如果每个异常过滤程序都返回EXCEPTION_CONTINUE_SEARCH时,会未到遇处理异常。
(2)调用SetUnhandledExceptionFilter安装用户提供的全局(顶层)异常过滤回调函数(为所有线程共享)。如果顶层异常回调函数返回EXCEPTION_EXECUTE_HANDLER或EXCEPTION_CONTINUE_SEARCH则直接传递给UnhandledExceptionFilter函数,UnhandledExceptionFilter根据这个返回值判断是终止进程还是重新执行异常代码。如果顶层异常回调函数返回EXCEPTION_CONTINUE_SEARCH,则接下来的要发生的事情就比较复杂(可参考后面的《UnhandledExceptionFilter内部工作流程》)
(3)SetUnhandledExceptionFilter返回值为上次安装的异常过滤程序的地址。如果使用C/C++运行库,则会默认安装一个__CxxUnhandledExceptionFilter过滤程序。该函数首先检查异常是不是C++异常,如果是则在结束时执行abort函数(该函数内部调用了UnhandledExceptionFilter函数,注意这可能会造成循环调用,因为UnhandledExceptionFilter内部调用了我们安装的全局异常过滤函数_CxxUnhandledExceptionFilter,而这个函数的内部又调用UnhandledExceptionFilter,为了防止无限递归调用,_CxxUnhandledExceptionFilter在调用UnhandledExceptionFilter之前会调用SetUnhandledExceptionFilter(NULL))。如果不是C++异常则返回EXCEPTION_CONTINUE_SEARCH。所以当我们调用SetUnhandled*函数,返回的地址为_CxxUnhandledExceptionFilter的地址。
(4)注意,在我们的顶层异常过滤函数里,在返回EXCEPTION_CONTINUE_SEARCH前,不应调用之前的全局异常过滤函数(即我们通过SetUnhandledExceptionFilter的返回值取得的那个函数)。因为如果这个函数是在某个动态库里,那它随时都可能被卸载了。
(5)如果SetUnhandledExceptionFilter(NULL),则取消我们设置的全局异常过滤函数。
【UnhandledExceptionFilter程序】演示设置顶层异常过滤函数
- #include <tchar.h>
- #include <windows.h>
- #include <locale.h>
- LONG WINAPI MyUnhandledExceptionFilter(
- struct _EXCEPTION_POINTERS *lpTopLevelExceptionFilter)
- {
- _tprintf(_T("发生未处理异常\n"));
- _tsystem(_T("PAUSE"));
- return EXCEPTION_EXECUTE_HANDLER; //这样返回,进程将被终止。
- }
- int _tmain()
- {
- SetUnhandledExceptionFilter(MyUnhandledExceptionFilter); //安装用户自定义的未处理异常
- _tsetlocale(LC_ALL, _T("chs"));
- __try{
- //SetErrorMode(SEM_NOGPFAULTERRORBOX);
- *(int*) = ;//引发异常
- }
- __except (EXCEPTION_CONTINUE_SEARCH){ //这里返回EXCEPTION_CONTINUE_SEARCH,异常就会到达MyUnhandled*
- }
- _tsystem(_T("PAUSE"));//这行不会被执行!
- return ;
- }
25.1.2 UnhandledExceptionFilter内部工作流程
①判断是否因为对资源进行写入操作引发的异常。如果是,将资源的只读属性改为可写入,并返回EXCEPTION_CONTINUE_EXECUTION以允许失败的指令再次执行。
②确定进程是否被调试。如果被调试,就返回EXCEPTION_CONTINUE_SEARCH给调试器,通知调试器定位异常指令,并告知我们出了什么样的异常。
③调用我们设置的顶层异常过滤函数(如果存在的话)。如果顶层过滤函数返回EXCEPT_EXECUTE_HANDLER或EXCEPTION_CONTINUE_EXECUTION,将直接传递给UnhandledExceptionFilter,由它将返回值给系统。如果返回EXCEPT_CONTINUE_SEARCH,则跳到第④步。
④再次将未处理异常报告给调试器
⑤终止进程:如果线程调用SetErrorMode并设置SEM_NOGPFAULTERRORBOX标志,那么UnhandledExceptFilter会返回EXCEPTION_EXECUTE_HANDLER,在未处理异常的情况下进行全局展开并执行未执行的finally块,然后进程终止。
如果没有调用SetErrorMode函数,UnhandledExceptionFilter会返回EXCEPTION_CONTINUE_SEARCH。于是系统内核得到程序控制,它将通过ALPC(高级本地过程调用)机制将异常通知给WerSvc(Windows错误报告专用服务),然后ALPC先阻塞自己的线程,直到WerSvc执行完毕。
⑥UnhandledExceptionFilter与WER的交互
当WerSvc接到通知时,会先调用CreateProcess来启动WerFault.exe,然后 WerSvc会等待这个新进程的结束。而WerFault.exe会向我们创建上面的两个对话框以报告错误的发生。当第1个对话框出现时,可以选择“取消”来终止我们的应用程序,否则过一会儿,会弹出第2个对话框,如果我们选择“关闭程序”,则WerFault.exe会调用TerminateProcess来结束我们的应用程序。如果选择“调试”,WerFault.exe会创建一个子进程(调试器),让他附着在出错的程序上进行“即时调试”
25.2 即时调试
(1)默认调试器:HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug子项下有一个名为Debugger的值,系统通过个值找到调试器。
(2)WerFault.exe会给这个调试器传入两个参数:要调试的进程ID和继承过来的事件句柄(这个句柄由WerSvc服务创建用于通知被调试进程调试也结束)
(3)通过将调试器附着到被调试进程,可以查看全局、局部和静态变量的值,也可以设置断点,检查函数调用树等调试工作。
【Spreadsheet程序】通过SEH向预订的地址空间稀疏调拨存储器
- /************************************************************************
- Module: Spreadsheet.cpp
- Notices:Copyright(c) 2008 Jeffrey Richter & Christophe Nasarre
- ************************************************************************/
- #include "../../CommonFiles/CmnHdr.h"
- #include "resource.h"
- #include "VMArray.h"
- #include <tchar.h>
- #include <strsafe.h>
- //////////////////////////////////////////////////////////////////////////
- HWND g_hWnd; //全局的窗口句柄,SEH报告中会用到
- const int g_nNumRows = ;
- const int g_nNumCols = ;
- //声明单个单元格内容的结构体,每个单元格大小为1024字节
- typedef struct{
- DWORD dwValue;
- BYTE bDummy[];
- }CELL,*PCELL;
- //声明全个电子表格的数据
- ////SPREADSHEET类型为一个数组类型,元素类型为CELL及g_nNumRows行g_nNumCols列。
- //判读时,可去掉typedef来看。
- typedef CELL SPREADSHEET[g_nNumRows][g_nNumCols];
- typedef SPREADSHEET* PSPREADSHEET;
- //////////////////////////////////////////////////////////////////////////
- //一个电子表格是一个二维数组的CELLs
- class CVMSpreadsheet :public CVMArray<CELL>{
- public:
- CVMSpreadsheet() :CVMArray<CELL>(g_nNumRows*g_nNumCols){}
- private:
- LONG OnAccessViolation(PVOID pvAddressTouched, BOOL bAttemptedRead, PEXCEPTION_POINTERS pep, BOOL bRetryUntilSuccessful);
- };
- //////////////////////////////////////////////////////////////////////////
- LONG CVMSpreadsheet::OnAccessViolation(PVOID pvAddressTouched, BOOL bAttemptedRead, PEXCEPTION_POINTERS pep, BOOL bRetryUntilSuccessful){
- TCHAR sz[];
- StringCchPrintf(sz, _countof(sz), TEXT("非法访问:试图在0x%8X进行%s操作!"),pvAddressTouched,
- bAttemptedRead ? TEXT("读取") : TEXT("写入"));
- SetDlgItemText(g_hWnd, IDC_LOG, sz);
- LONG lDispostion = EXCEPTION_EXECUTE_HANDLER;
- //只有写入操作发生异常时才会提交物理存储器,读取操作则不会
- if (!bAttemptedRead){
- //返回基类的返回值
- lDispostion = CVMArray<CELL>::OnAccessViolation(pvAddressTouched,
- bAttemptedRead, pep, bRetryUntilSuccessful);
- }
- return (lDispostion);
- }
- //////////////////////////////////////////////////////////////////////////
- //产生一个全局CVMSpreadsheet对象
- static CVMSpreadsheet g_ssObject;
- //创建一个全局指针,指向电子表格的区域
- SPREADSHEET& g_ss = *(PSPREADSHEET)(PCELL)g_ssObject;
- //////////////////////////////////////////////////////////////////////////
- BOOL Dlg_OnInitDialog(HWND hWnd, HWND hWndFocus, LPARAM lParam){
- chSETDLGICONS(hWnd, IDI_SPREADSHEET);
- g_hWnd = hWnd; //保存句柄(For SEH错误报告)
- //设置对话框上面控件的默认值
- Edit_LimitText(GetDlgItem(hWnd, IDC_ROW), );
- Edit_LimitText(GetDlgItem(hWnd, IDC_COLUMN), );
- Edit_LimitText(GetDlgItem(hWnd, IDC_VALUE), );
- SetDlgItemInt(hWnd, IDC_ROW, , FALSE);
- SetDlgItemInt(hWnd, IDC_COLUMN, , FALSE);
- SetDlgItemInt(hWnd, IDC_VALUE, , FALSE);
- return (TRUE);
- }
- //////////////////////////////////////////////////////////////////////////
- void Dlg_OnCommand(HWND hWnd, int id, HWND hwndCtrl, UINT codeNotity){
- int nRow, nCol;
- switch (id)
- {
- case IDCANCEL:
- EndDialog(hWnd, id);
- break;
- case IDC_ROW:
- //用户修改了行数,更新UI
- nRow = GetDlgItemInt(hWnd, IDC_ROW, NULL, FALSE);
- EnableWindow(GetDlgItem(hWnd, IDC_READCELL), chINRANGE(, nRow, g_nNumRows - ));
- EnableWindow(GetDlgItem(hWnd, IDC_WRITECELL), chINRANGE(, nRow, g_nNumRows - ));
- break;
- case IDC_COLUMN:
- //用户修改了行数,更新UI
- nCol = GetDlgItemInt(hWnd, IDC_COLUMN, NULL, FALSE);
- EnableWindow(GetDlgItem(hWnd, IDC_READCELL), chINRANGE(, nCol, g_nNumCols - ));
- EnableWindow(GetDlgItem(hWnd, IDC_WRITECELL), chINRANGE(, nCol, g_nNumCols - ));
- break;
- case IDC_READCELL:
- //尝试从用户选择的单元格中读取一个数据
- SetDlgItemText(hWnd, IDC_LOG, TEXT("没有发生异常!"));
- nRow = GetDlgItemInt(hWnd, IDC_ROW, NULL, FALSE);
- nCol = GetDlgItemInt(hWnd, IDC_COLUMN, NULL, FALSE);
- __try{
- SetDlgItemInt(hWnd, IDC_VALUE, g_ss[nRow][nCol].dwValue, FALSE);
- }
- //ExceptionFilter返回EXECUTION_CONTINUE_EXECUTE或EXCEPTION_EXECUTE_HANDLER
- //如果提交成功,返回前者;失败,返回后者
- __except (g_ssObject.ExceptionFilter(GetExceptionInformation(),FALSE)){
- //单元格不支持存储,里面不含内容
- SetDlgItemText(hWnd, IDC_VALUE, TEXT(""));
- }
- break;
- case IDC_WRITECELL:
- //尝试向用户选择的单元格中写入数据
- SetDlgItemText(g_hWnd, IDC_LOG, TEXT("没有发生异常!"));
- nRow = GetDlgItemInt(hWnd, IDC_ROW, NULL, FALSE);
- nCol = GetDlgItemInt(hWnd, IDC_COLUMN, NULL, FALSE);
- //假如单元格不支持存储,将抛出非法内存访问,这时将导致自动提交存储器
- //这里不设置try/except,则异常会让全局异常(未处理)过滤函数捕获
- g_ss[nRow][nCol].dwValue = GetDlgItemInt(hWnd, IDC_VALUE, NULL, FALSE);
- break;
- }
- }
- //////////////////////////////////////////////////////////////////////////
- INT_PTR WINAPI Dlg_Proc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam){
- switch (uMsg)
- {
- chHANDLE_DLGMSG(hWnd, WM_INITDIALOG, Dlg_OnInitDialog);
- chHANDLE_DLGMSG(hWnd, WM_COMMAND, Dlg_OnCommand);
- }
- return (FALSE);
- }
- //////////////////////////////////////////////////////////////////////////
- int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nShowCmd){
- DialogBox(hInstance, MAKEINTRESOURCE(IDD_SPREADSHEET), NULL, Dlg_Proc);
- return ();
- }
//VMArray.h
- /************************************************************************
- Module: VMArray.h
- Notices:Copyright(c) 2008 Jeffrey Richter & Christophe Nasarre
- ************************************************************************/
- #pragma once
- #include "../../CommonFiles/CmnHdr.h"
- #include <tchar.h>
- #ifndef _M_IX86
- #error "The following code only works for x86!"
- #endif
- //////////////////////////////////////////////////////////////////////////
- //注意:这个C++类是线程不安全的。不能在多线程下同时创建和销毁该类的实例
- //但是一旦创建,多线程可同时访问不同的CVMArray对象,也可以通过自己同步的
- //方法在多线程下访问同一个CVMArray对象
- //////////////////////////////////////////////////////////////////////////
- template <class TYPE>
- class CVMArray{
- public:
- //为数组各元素预订稀疏的地址空间
- CVMArray(DWORD dwreservElements);
- //释放
- virtual ~CVMArray();
- //允许访问数组中的一个元素
- operator TYPE*(){ return (m_pArray); }
- operator const TYPE*()const { return (m_pArray); }
- //若提交失败,可以被优雅的处理
- LONG ExceptionFilter(PEXCEPTION_POINTERS pep, BOOL bRetryUntilSuccessful = FALSE);
- protected:
- //虚函数,当非法访问内存时,可优雅的处理
- virtual LONG OnAccessViolation(PVOID pvAddressTouched, BOOL bAttemptedRead,
- PEXCEPTION_POINTERS pep, BOOL bRetryUntilSuccessful);
- private:
- static CVMArray* sm_pHead; //第一个CVMArray对象
- CVMArray* m_pNext; //下一个VCMArray对象
- TYPE* m_pArray; //指向一个预订的区域数组
- DWORD m_cbReserve; //预订的数组空间的大小
- private:
- //访问前一个未处理异常过滤函数
- static PTOP_LEVEL_EXCEPTION_FILTER sm_pfnUnhandledExceptionFilterPrev;
- //当这个类发生异常,调用我们自己的全局异常过滤函数(VS2005以后微软让
- // 对CRT (C 运行时库)的一些与安全相关的代码做了些改动,使得许多错误
- //都不能在SetUnhandledExceptionFilter 捕获到。
- static LONG WINAPI MyUnhandledExceptionFilter(PEXCEPTION_POINTERS pep);
- void DisableSetUnhandledExceptionFilter();//使SetUnhandledExceptionFilter函数失效
- //为了达到设置全局异常过滤函数的目的,用向量化
- //AddVectoredContinueHandler来达到设置全局异常过滤函数相同的功能
- //static LONG WINAPI LastVEHandler(PEXCEPTION_POINTERS pep);
- //static PVOID sm_pVEH;
- };
- //////////////////////////////////////////////////////////////////////////
- //向量化异常过滤函数句柄
- //template <class TYPE>
- //PVOID CVMArray<TYPE>::sm_pVEH = NULL;
- //CVMArray对象链表的头
- template <class TYPE>
- CVMArray<TYPE>* CVMArray<TYPE>::sm_pHead = NULL;
- //前一个全局异常过滤函数
- template <class TYPE>
- PTOP_LEVEL_EXCEPTION_FILTER CVMArray<TYPE>::sm_pfnUnhandledExceptionFilterPrev;
- //////////////////////////////////////////////////////////////////////////
- template <class TYPE>
- CVMArray<TYPE>::CVMArray(DWORD dwreservElements){
- if (sm_pHead == NULL){
- //在创建第1个对象前,安装我们的全局异常过滤函数
- sm_pfnUnhandledExceptionFilterPrev =
- SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);
- DisableSetUnhandledExceptionFilter();//使SetUnhandledExceptionFilter失效
- //sm_pVEH = AddVectoredContinueHandler(0, LastVEHandler);
- }
- m_pNext = sm_pHead; //下一次节点初始化为链表头部
- sm_pHead = this; //本对象为链表头
- m_cbReserve = sizeof(TYPE)*dwreservElements;
- //预订整个数组大小的一块区域
- m_pArray = (TYPE*)VirtualAlloc(NULL, m_cbReserve, MEM_RESERVE | MEM_TOP_DOWN,
- PAGE_READWRITE);
- chASSERT(m_pArray != NULL);
- }
- //////////////////////////////////////////////////////////////////////////
- template <class TYPE>
- CVMArray<TYPE>::~CVMArray(){
- //释放数组所占的空间
- VirtualFree(m_pArray, , MEM_RELEASE);
- //删除链表
- CVMArray* p = sm_pHead;
- if (p == this){
- sm_pHead = p->m_pNext; //删除链表头
- }else{
- BOOL bFound = FALSE;
- //遍历链头,并修复指针
- for (; !bFound && (p->m_pNext != NULL);p= p->m_pNext){
- if (p->m_pNext == this){
- p->m_pNext = p->m_pNext->m_pNext;
- bFound = TRUE;
- break;
- }
- }
- chASSERT(bFound);
- }
- //if (sm_pVEH != NULL)
- // RemoveVectoredExceptionHandler(sm_pVEH);
- }
- //////////////////////////////////////////////////////////////////////////
- //当非法访问时,默认的异常处理
- template <class TYPE>
- LONG CVMArray<TYPE>::OnAccessViolation(PVOID pvAddressTouched, BOOL bAttemptedRead, PEXCEPTION_POINTERS pep, BOOL bRetryUntilSuccessful)
- {
- BOOL bCommittedStorage = FALSE; //假定提交失败
- do{
- //提交存储器
- bCommittedStorage = (NULL != VirtualAlloc(pvAddressTouched,
- sizeof(TYPE),MEM_COMMIT,PAGE_READWRITE));
- //假如无法提交而我们又试图重试,提醒用户释放内存
- if (!bCommittedStorage && bRetryUntilSuccessful)
- MessageBox(NULL, TEXT("请关闭一些其他应用,然后按“确定”!"),
- TEXT("内存空间不足"),MB_ICONWARNING | MB_OK);
- } while (!bCommittedStorage && bRetryUntilSuccessful);
- //当提交存储器,重新执行出错代码。否则执行异常处理程序
- return (bCommittedStorage
- ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_EXECUTE_HANDLER);
- }
- //////////////////////////////////////////////////////////////////////////
- //过滤函数被关联到单一的CVMArray对象
- template <class TYPE>
- LONG CVMArray<TYPE>::ExceptionFilter(PEXCEPTION_POINTERS pep, BOOL bRetryUntilSuccessful /* = FALSE */){
- //默认,提交给其它过滤函数处理(这是一个安全的选择)
- LONG lDispostion = EXCEPTION_CONTINUE_SEARCH;
- //只修改非法访问内存的异常
- if (pep->ExceptionRecord->ExceptionCode != EXCEPTION_ACCESS_VIOLATION)
- return (lDispostion);
- //获取试图访问的地址,以及读或写异常
- //对于EXCEPTION_ACCESS_VIOLATION异常,ExceptionInformation[0]指出非法访问的类型
- //0表示线程试图读取不能访问的数据;1表示写入不能访问的数据
- PVOID pvAddrTouched = (PVOID)pep->ExceptionRecord->ExceptionInformation[];//非法访问的地址
- BOOL bAttempedRead = (pep->ExceptionRecord->ExceptionInformation[] == ); //非法访问的类型
- //如果试图访问的地址在VMArray的预订的地址空间内
- if ((m_pArray <=pvAddrTouched) && (pvAddrTouched<((PBYTE)m_pArray + m_cbReserve))){
- //访问这个数组,并尝试解决问题
- lDispostion = OnAccessViolation(pvAddrTouched, bAttempedRead, pep, bRetryUntilSuccessful);
- }
- return (lDispostion);
- }
- //////////////////////////////////////////////////////////////////////////
- //template <class TYPE>
- //LONG CVMArray<TYPE>::LastVEHandler(PEXCEPTION_POINTERS pep){
- // //默认为让其他过滤器处理
- // LONG lDispostion = EXCEPTION_CONTINUE_SEARCH;
- //
- // MessageBox(NULL, TEXT("发生未处理异常"), TEXT("提示"), MB_OK);
- //
- // //只修改非法访问内存
- // if (pep->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION){
- // //遍历所有链表节点
- // for (CVMArray* p = sm_pHead; p != NULL; p = p->m_pNext){
- // //询问该节点是否可以修复错误
- // //注意:这个错误必须被修复,否进程会被终止
- // lDispostion = p->ExceptionFilter(pep, TRUE);
- //
- // //如果修复了错误,就停止循环
- // if (lDispostion != EXCEPTION_CONTINUE_SEARCH)
- // break;
- // }
- // }
- // return (lDispostion);
- //}
- //新版本的CRT 实现在异常处理中强制删除所有应用程序先前设置的捕获函数,如下所示:
- ///* Make sure any filter already in place is deleted. */
- //SetUnhandledExceptionFilter(NULL);
- //UnhandledExceptionFilter(&ExceptionPointers);
- //解决方法是拦截CRT 调用SetUnhandledExceptionFilter 函数,使之无效
- template <class TYPE>
- void CVMArray<TYPE>::DisableSetUnhandledExceptionFilter()
- {
- void *addr = (void*)GetProcAddress(LoadLibrary(_T("kernel32.dll")),
- "SetUnhandledExceptionFilter");
- if (addr)
- {
- unsigned char code[];
- int size = ;
- code[size++] = 0x33;
- code[size++] = 0xC0;
- code[size++] = 0xC2;
- code[size++] = 0x04;
- code[size++] = 0x00;
- DWORD dwOldFlag, dwTempFlag;
- VirtualProtect(addr, size, PAGE_READWRITE, &dwOldFlag);
- WriteProcessMemory(GetCurrentProcess(), addr, code, size, NULL);
- VirtualProtect(addr, size, dwOldFlag, &dwTempFlag);
- }
- }
- //////////////////////////////////////////////////////////////////////////
- //全局异常过滤函数,为所有CVMArray对象共用
- //这个未处理异常很有必须,如果用户忘记用try/except来处理此类对象发生的异常,可以在这里
- //进行最后的处理!
- template <class TYPE>
- LONG WINAPI CVMArray<TYPE>::MyUnhandledExceptionFilter(PEXCEPTION_POINTERS pep){
- //默认为让其他过滤器处理
- LONG lDispostion = EXCEPTION_CONTINUE_SEARCH;
- //只修改非法访问内存
- if (pep->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION){
- //遍历所有链表节点
- for (CVMArray* p = sm_pHead; p != NULL;p=p->m_pNext){
- //询问该节点是否可以修复错误
- //注意:这个错误必须被修复,否进程会被终止
- lDispostion = p->ExceptionFilter(pep, TRUE);
- //如果修复了错误,就停止循环
- if (lDispostion != EXCEPTION_CONTINUE_SEARCH)
- break;
- }
- }
- //如果节点修复错误,试图调用前一个异常处理来处理
- if (lDispostion == EXCEPTION_CONTINUE_SEARCH)
- lDispostion = sm_pfnUnhandledExceptionFilterPrev(pep);
- return (lDispostion);
- }
- ///////////////////////////////////文件结束///////////////////////////////
//resource.h
- //{{NO_DEPENDENCIES}}
- // Microsoft Visual C++ 生成的包含文件。
- // 供 25_Spreadsheet.rc 使用
- //
- #define IDD_SPREADSHEET 1
- #define IDC_LOG 101
- #define IDI_SPREADSHEET 102
- #define IDI_ICON1 102
- #define IDC_ROW 1001
- #define IDC_COLUMN 1002
- #define IDC_COLUMN2 1003
- #define IDC_VALUE 1003
- #define IDC_READCELL 1004
- #define IDC_WRITECELL 1005
- // Next default values for new objects
- //
- #ifdef APSTUDIO_INVOKED
- #ifndef APSTUDIO_READONLY_SYMBOLS
- #define _APS_NEXT_RESOURCE_VALUE 103
- #define _APS_NEXT_COMMAND_VALUE 40001
- #define _APS_NEXT_CONTROL_VALUE 1001
- #define _APS_NEXT_SYMED_VALUE 101
- #endif
- #endif
//Spreadsheet.rc
- // Microsoft Visual C++ generated resource script.
- //
- #include "resource.h"
- #define APSTUDIO_READONLY_SYMBOLS
- /////////////////////////////////////////////////////////////////////////////
- //
- // Generated from the TEXTINCLUDE 2 resource.
- //
- #include "winres.h"
- /////////////////////////////////////////////////////////////////////////////
- #undef APSTUDIO_READONLY_SYMBOLS
- /////////////////////////////////////////////////////////////////////////////
- // 中文(简体,中国) resources
- #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
- LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
- #ifdef APSTUDIO_INVOKED
- /////////////////////////////////////////////////////////////////////////////
- //
- // TEXTINCLUDE
- //
- TEXTINCLUDE
- BEGIN
- "resource.h\0"
- END
- TEXTINCLUDE
- BEGIN
- "#include ""winres.h""\r\n"
- "\0"
- END
- TEXTINCLUDE
- BEGIN
- "\r\n"
- "\0"
- END
- #endif // APSTUDIO_INVOKED
- /////////////////////////////////////////////////////////////////////////////
- //
- // Dialog
- //
- IDD_SPREADSHEET DIALOGEX , , ,
- STYLE DS_SETFONT | DS_CENTER | WS_MINIMIZEBOX | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
- CAPTION "Spreadsheet"
- FONT , "宋体", , , 0x86
- BEGIN
- LTEXT "单元格大小:\n行数:\n列数:\n总大小:",IDC_STATIC,,,,
- LTEXT "1024 字节\n256\n1024\n256 MB (268,435,456 字节)",IDC_STATIC,,,,
- LTEXT "行 (0-255):",IDC_STATIC,,,,
- EDITTEXT IDC_ROW,,,,,ES_AUTOHSCROLL | ES_NUMBER
- LTEXT "列(0-1023):",IDC_STATIC,,,,
- EDITTEXT IDC_COLUMN,,,,,ES_AUTOHSCROLL | ES_NUMBER
- PUSHBUTTON "&读取单元格",IDC_READCELL,,,,
- LTEXT "值:",IDC_STATIC,,,,
- EDITTEXT IDC_VALUE,,,,,ES_AUTOHSCROLL | ES_NUMBER
- PUSHBUTTON "写入单元格",IDC_WRITECELL,,,,
- LTEXT "操作日志:",IDC_STATIC,,,,
- EDITTEXT IDC_LOG,,,,,ES_MULTILINE | ES_AUTOHSCROLL | ES_READONLY
- END
- /////////////////////////////////////////////////////////////////////////////
- //
- // DESIGNINFO
- //
- #ifdef APSTUDIO_INVOKED
- GUIDELINES DESIGNINFO
- BEGIN
- IDD_SPREADSHEET, DIALOG
- BEGIN
- END
- END
- #endif // APSTUDIO_INVOKED
- /////////////////////////////////////////////////////////////////////////////
- //
- // Icon
- //
- // Icon with lowest ID value placed first to ensure application icon
- // remains consistent on all systems.
- IDI_SPREADSHEET ICON "Spreadsheet.ico"
- #endif // 中文(简体,中国) resources
- /////////////////////////////////////////////////////////////////////////////
- #ifndef APSTUDIO_INVOKED
- /////////////////////////////////////////////////////////////////////////////
- //
- // Generated from the TEXTINCLUDE 3 resource.
- //
- /////////////////////////////////////////////////////////////////////////////
- #endif // not APSTUDIO_INVOKED
25.3 向量化异常和继续处理程序
25.3.1向量化异常(vectored exception handing,VEH)——在SEH前被调用
①对于多层嵌套的SEH来说,外层的__try_except语句块可能没有机会处理被内层嵌套拦截的异常。对于一般软件来说,这不是太大的问题,但是当内层嵌套的软件是第三方的库函数,并且内部以不友好的方式处理了异常,比如:异常退出进程了事,这对整个程序将造成很不利的影响。
②此时可以利用向量化异常处理,在正常的SEH之前以合适的方式拦截和处理异常。
③当异常发生时,系统在执行SEH过滤程序之前,会先依次调用VEH列表中的每个VEH异常处理函数。
(2)注册VEH异常处理程序:AddVectoredExceptionHandler(bFirstInTheList,pfnHandler)
①参数bFirstInTheList为0表示pfnHandler被添加到列表尾端,非0在列表头部。
②pfnHandler:异常处理函数,如果返回EXCEPTION_CONTINUE_SEARCH,则重新执行导致异常的指令,如果返回EXCEPTION_CONTINUE_SEARCH表示让VEH链表中的其他函数去处理异常,如果所有函数都返回EXCEPTION_CONTINUE_SEARCH,SEH过滤函数就会被执行。
(3)删除VEH异常处理函数:RemoveVectoredExceptionHandler(pHandler),其中pHandler这个句柄为AddVectoredExceptionHandler的返回值。
25.3.2 继续处理程序:——用于实现程序的诊断和跟踪
(1)安装:PVOID AddVectoredContinueHandler(bFirstInTheList,pfnHandler);
①参数bFirstInTheList为0,表示安装在继续处理程序列头的尾部,非0在头部。
②通过该函数安装的异常处理程序是在SetUnhandledExceptionFilter安装的异常处理程序返回EXCEPTION_CONTINUE_SEARCH之后才被调用。
③如果pfnHandler函数返回EXCEPTION_CONTINUE_EXECUTION重新执行导致异常的指令,EXCEPTION_CONTINUE_SEARCH让系统执行它后面的异常处理程序。
(2)删除:RemoveVectoredContinueHandler(pHandler);
【VectoredExceptionFilter】演示向量化异常过滤函数的调用
- #include <windows.h>
- #include <tchar.h>
- #include <locale.h>
- int g_iVal = ;
- //VEH1异常过滤函数
- LONG CALLBACK VEH1(struct _EXCEPTION_POINTERS* pEP){
- _tprintf(_T("VEH1\n"));
- return EXCEPTION_CONTINUE_SEARCH;
- }
- //VEH2异常过滤函数
- LONG CALLBACK VEH2(struct _EXCEPTION_POINTERS* pEP){
- _tprintf(_T("VEH2\n"));
- //以下的注释,可以取消以观察不同的输出结果
- //if ((EXCEPTION_INT_DIVIDE_BY_ZERO == pEP->ExceptionRecord->ExceptionCode)){
- // g_iVal = 25;
- // return EXCEPTION_CONTINUE_EXECUTION;
- //}
- return EXCEPTION_CONTINUE_SEARCH;
- }
- //VEH3异常过滤函数
- LONG CALLBACK VEH3(struct _EXCEPTION_POINTERS* pEP){
- _tprintf(_T("VEH3\n"));
- return EXCEPTION_CONTINUE_SEARCH;
- }
- //SEH异常过滤函数
- LONG SEHFilter(PEXCEPTION_POINTERS pEP){
- _tprintf(_T("SEH\n"));
- if ((EXCEPTION_INT_DIVIDE_BY_ZERO == pEP->ExceptionRecord->ExceptionCode)){
- g_iVal = ;
- return EXCEPTION_CONTINUE_EXECUTION;
- }
- return EXCEPTION_CONTINUE_SEARCH;
- }
- void Fun1(int iVal){
- __try{
- _tprintf(_T("Fun1 g_iVal = %d iVal = %d\n"), g_iVal, iVal);
- iVal /= g_iVal; //这里发生异常,VEH异常会先被调用!
- _tprintf(_T("Fun1 g_iVal = %d iVal = %d\n"), g_iVal, iVal);
- }
- __except (EXCEPTION_EXECUTE_HANDLER){
- _tprintf(_T("Func1 _except块执行,程序将退出!\n"));
- _tsystem(_T("PAUSE"));
- ExitProcess();
- }
- }
- int _tmain(){
- _tsetlocale(LC_ALL, _T("chs"));
- PVOID pVEH1 = AddVectoredExceptionHandler(, VEH1);//安装到VEH链表尾部
- PVOID pVEH2 = AddVectoredExceptionHandler(, VEH2);//安装到VEH链表尾部
- PVOID pVEH3 = AddVectoredExceptionHandler(, VEH3);//安装到VEH链表尾部
- __try{
- Fun1(g_iVal);
- }
- __except (SEHFilter(GetExceptionInformation())){
- _tprintf(_T("main _except excuted!\n"));
- }
- RemoveVectoredExceptionHandler(pVEH1);
- RemoveVectoredExceptionHandler(pVEH2);
- RemoveVectoredExceptionHandler(pVEH3);
- _tsetlocale(LC_ALL, _T("chs"));
- _tsystem(_T("PAUSE"));
- return ;
- }
25.4 C++异常与结构化异常的比较
(1)框架的差别
//C++异常 void ChunkyFunky(){ try{ //try块 ... throw 5; } catch (int x){ //catch块 ... } ... } |
//SEH异常 void ChunkyFunky(){ __try{ //try块 //... RaiseException(Code = 0xE06D7363,//ASCII的“msc” Flag = EXECEPTION_NONCONTINUABLE, Args = 5); } __except ((ArgType == Integer)? EXCEPTION_EXECUTE_HANDLER: EXCEPTION_CONTINUE_SEARCH){ //Catch块 ... } ... } |
(2)SEH和C++异常的比较
①SEH是操作系统提供的,它在任何语言中都可以使用,而C++异常只有在编写C++代码时才可以使用。
②如果开发C++应用程序,应该使用C++异常,而不是SEH异常,因为C++异常是语言的一部分,编译器会自动生成代码来调用C++对象的析构函数,保证对象的释放。
③C++异常也是利用操作系统的SEH来实现的,所以在创建一个C++try块时,编译器也会生成一个SEH的__try块。C++的catch语句对应SEH异常过滤程序,catch块中的代码对应SEH __except块中的代码。C++的throw语句也是对RaiseException函数的调用。
④C++调用throw抛出异常时,都会自动带EXCEPT_NONCONTINUEABLE标志。这意味着C++不能再次执行错误代码。
⑤__except通过比较throw变量的数据类型与C++ catch语句中所用到的变量的数据类型,如果一致,返回EXCEPTION_EXECUTE_HANDLER,让__except块执行。如果不同,返回EXCEPTION_CONTINUE_SEARCH继续向搜索外层的__try/__except。
25.5 异常与调试器
(1)首次机会通知和最后机会通知:
当某个线程抛出异常里,操作系统会马上通知调试器(如果调试器己经附着),这个通知被称为“首次机会通知(First-Chance Notification)”调试器将响应这个通知,促使线程寻找异常过滤程序。如果所有的异常过滤程序都返回EXCEPTION_CONTINUE_SEARCH,操作系统会给调试器一个“最后机会通知(Last-Chance Notification)”
(2)每个解决方案(.sln),可以从主菜单“调试”→“异常(x)…”找出“异常”对话框。每个异常代码为32位,如果勾选“引发”表示每当被调试线程引发异常时,调试器都会收到首次机会通知,此时异常刚刚发生,线程还没有得到机会执行异常过滤程序。调试器会弹出相应的对话框让我们选择操作,我们可以选择调试,然后在代码里设置断点,查看变量的值或线程的函数调用栈,如果不勾选“引发”这项,则不会弹出对话框,但调试器收到通知时,会在输出窗口中显示一行文字,以表示它收到了这个异常通知,然后允许线程寻找异常过滤程序。 “用户未处理的”表示异常过滤函数返回EXCEPTION_CONTINUE_SEARCH,调试器收到最后机会通知,也会弹出相应的对话框让用户来选择操作。
(3)可以自定义软件异常:只需单击“添加”按钮,然后输入异常名称和异常代码(不能与己有的重复)
第25章 SEH结构化异常处理_未处理异常及向量化异常的更多相关文章
- 第24章 SEH结构化异常处理_异常处理及软件异常
24.1 程序的结构 (1)try/except框架 __try{ //被保护的代码块 …… } __except(except fileter/*异常过滤程序*/){ //异常处理程序 } (2) ...
- 第23章 SEH结构化异常处理(2)_编译器对系统SEH机制的封装
23.2 编译器层面对系统SEH机制的封装 23.2.1 扩展的EXCEPTION_REGISTRATION级相关结构:VC_EXCEPTION_REGISTRATION (1)VC_EXCEPTIO ...
- 第23章 SEH结构化异常处理(1)_系统SEH机制
23.1 基础知识 23.1.1 Windows下的软件异常 (1)中断和异常 ①中断是由外部硬件设备或异步事件产生的 ②异常是由内部事件产生的,可分为故障.陷阱和终止三类. (2)两种异常处理机制: ...
- 第23章 SEH结构化异常处理(3)_终止处理程序
23.3 终止处理程序 23.3.1 程序的结构 (1)框架 __try{ //被保护的代码块 …… } __finally{ //终止处理 } (2)__try/__finally的特点 ①fina ...
- 异常处理第三讲,SEH(结构化异常处理),异常展开问题
异常处理第三讲,SEH(结构化异常处理),异常展开问题 作者:IBinary出处:http://www.cnblogs.com/iBinary/版权所有,欢迎保留原文链接进行转载:) 不知道昨天有木有 ...
- Windows内核读书笔记——SEH结构化异常处理
SEH是对windows系统中的异常分发和处理机制的总称,其实现分布在很多不同的模块中. SEH提供了终结处理和异常处理两种功能. 终结处理保证终结处理块中的程序一定会被执行 __try { //要保 ...
- C#如何使用结构化异常处理
Knowledge Base: Chinese (Simplified) 如何使用 Visual C# .NET 和 Visual C# 2005 中的结构化异常处理文章ID: 816157 最近更新 ...
- 深入研究 Win32 结构化异常处理(作者博客有许多SEH的研究文章)
摘要 就像人们常说的那样,Win32 结构化异常处理(SEH)是一个操作系统提供的服务.你能找到的所有关于 SEH 的文档讲的都是针对某个特定编译器的.建立在操作系统层之上的封装库.我将从 SEH 的 ...
- [C++]深入解析结构化异常处理(SEH)
http://www.cppblog.com/weiym/archive/2015/02/27/209884.html 尽管以前写过一篇SEH相关的文章<关于SEH的简单总结>, 但那真的 ...
随机推荐
- ahjesus fstab修改错误了如何修复
fstab修改错误了如何修复 当你不小心把磁盘表输入错误以后,系统总是让你按ctrl+D重新启动或者输入密 码进入shell,你输入密码登陆后, 编辑文件是只读的,执行下面的命令后就可以编辑了 ...
- [Tips] Useful link ... on going
1. CSS Bootstrap http://getbootstrap.com/ Bootstrap 中文文档 http://getbootstrap.com/2.3.2/ 最全的 Twitter ...
- setTimeout实现动画的黄金优化法则
1.使用递归思想实现setTimeout的轮询动画:在每一次执行方法的时候都重新的设置一个定时器,然后在指定时间内重新的执行当前的方法 问题:每一次设置的定时器,虽然不执行了,但是还存在呢,浪费性能 ...
- DevExpress.XtraGrid.Views 设置指定行的背景颜色 .
如需要将指定行的背景设置颜色,可参考以下示例 1.事件:CustomDrawCell 2.示例: private void gridView1_CustomDrawCell(object sender ...
- SharePoint 2013: Search Architecture in SPC202
http://social.technet.microsoft.com/wiki/contents/articles/15989.sharepoint-2013-search-architecture ...
- Force.com微信开发系列(五)自定义菜单进阶及语音识别
在上文里我们介绍了如何通过Force.com平台里为微信账号添加自定义菜单,本文里我们将进一步介绍如何查询菜单以及删除菜单的相关知识,最后会介绍微信平台如何进行语音识别的相关技术. 查询菜单 与创建菜 ...
- 换iphone5屏幕你花了多少钱?不防我们看下市场的批发价格
看来人家批发价也不便宜啊,你修一块花了多少米 免费b2b平台 US $1 - 79.99 / Piece Factory Price for iphone 5" lcd alibaba ...
- STL--迭代器(iterator)
指针与数组 指针与其它数据结构呢?比如说链表? 存储空间是非连续的.不能通过对指向这种数据结构的指针做累加来遍历. 能不能提供一个行为类似指针的类,来对非数组的数据结构进行遍历呢?这样我们就能够以同样 ...
- 关于Block Formatting Context--BFC和IE的hasLayout
转文请标明 --- 出处:穆乙 http://www.cnblogs.com/pigtail/ 一.BFC是什么? BFC(Block Formatting Context)直译为"块级格式 ...
- Dev Grid拖拽移动行
效果图 源码下载 拖拽时带行截图效果实现代码 /// <summary> /// 拖拽帮助类 /// </summary> public static class DragHe ...