mfc异常崩溃处理
2014-01-03 10:14
323 查看
本文将完成一个监控和处理mfc程序异常崩溃后自动重启的实例,同时建议所有异常都应查找原因(例如内存溢出、野指针操作等等,他们在编译时都无法发现),本着人性化的处理流程,你也需要一个对异常崩溃的处理工作。以下是详细步骤:
返回值可以有以下几种情况,视具体情况而定:
EXCEPTION_EXECUTE_HANDLER equ 1 表示我已经处理了异常,可以优雅地结束了
EXCEPTION_CONTINUE_SEARCH equ 0 表示我不处理,其他人来吧,于是windows调用默认的处理程序显示一个错误框,并结束
EXCEPTION_CONTINUE_EXECUTION equ -1 表示错误已经被修复,请从异常发生处继续执行
1.2 在程序初始化的时候,例如OnInitDialog中,声明当程序异常崩溃时调用我们自定义的回调函数:
1.3 模拟一次崩溃,例如本例中,新建一个按钮,并处理一次异常操作:
编译运行,当test.exe程序执行了异常代码后,会试图打开同级目录下的restart.exe程序。
本例的restart.exe将试图重启发生的异常程序。
2.2 重启一个程序,需要用到关闭一个进程、一个启动程序的程序(相关头文件里的声明以下均省略):
2.3 对ini文件的操作,以及时间戳的操作,需要用到以下方法:
2.4 需要用到的方法准备就绪,下面完成一个重启程序流程(本例将尝试在30秒内重启异常2次,读者应视具体情况自定流程):
2.5 最后在该dialog的OnInitDialog初始化中调用一下自定义的重启流程:
2.6 编译运行,本例结构如下:
1.新建一个基于对话框的dialog(本例test.exe)
1.1 在testDlg.cpp中添加一个我们自定义的回调函数,用来处理mfc程序的异常崩溃:// CtestDlg 对话框 LONG WINAPI FreeEIM_UnhandledExceptionFilter(LPEXCEPTION_POINTERS ExceptionInfo) { AfxMessageBox(L"Exception"); //调用回调函数成功 //调用重启/错误提交程序(视情况而定) CString strPath = _T("restart.exe"); USES_CONVERSION; LPCSTR lpcs = NULL; lpcs = T2A(strPath.GetBuffer(strPath.GetLength())); WinExec(lpcs, SW_SHOWNORMAL); return EXCEPTION_EXECUTE_HANDLER; //返回本回调函数的处理结果 }
返回值可以有以下几种情况,视具体情况而定:
EXCEPTION_EXECUTE_HANDLER equ 1 表示我已经处理了异常,可以优雅地结束了
EXCEPTION_CONTINUE_SEARCH equ 0 表示我不处理,其他人来吧,于是windows调用默认的处理程序显示一个错误框,并结束
EXCEPTION_CONTINUE_EXECUTION equ -1 表示错误已经被修复,请从异常发生处继续执行
1.2 在程序初始化的时候,例如OnInitDialog中,声明当程序异常崩溃时调用我们自定义的回调函数:
BOOL CtestDlg::OnInitDialog() { CDialog::OnInitDialog(); // 将“关于...”菜单项添加到系统菜单中。 // IDM_ABOUTBOX 必须在系统命令范围内。 ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE); if (pSysMenu != NULL) { CString strAboutMenu; strAboutMenu.LoadString(IDS_ABOUTBOX); if (!strAboutMenu.IsEmpty()) { pSysMenu->AppendMenu(MF_SEPARATOR); pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); } } // 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动 // 执行此操作 SetIcon(m_hIcon, TRUE); // 设置大图标 SetIcon(m_hIcon, FALSE); // 设置小图标 // TODO: 在此添加额外的初始化代码 SetUnhandledExceptionFilter(FreeEIM_UnhandledExceptionFilter); //声明调用回调函数 return TRUE; // 除非将焦点设置到控件,否则返回 TRUE }
1.3 模拟一次崩溃,例如本例中,新建一个按钮,并处理一次异常操作:
void CtestDlg::OnBnClickedButton1() { // TODO: 在此添加控件通知处理程序代码 int* ptr = NULL; *ptr = 3; }
编译运行,当test.exe程序执行了异常代码后,会试图打开同级目录下的restart.exe程序。
本例的restart.exe将试图重启发生的异常程序。
2.新建一个基于对话框的dialog(本例restart.exe)
2.1 本例将用一个ini文件记录下每次重启发生的时间,声明一下ini文件名和需要重启的程序名,添加一个引用,本例默认他们都在同级目录下:#include <Tlhelp32.h>
const CString PRO_NAME = _T("test_restart.exe");//_T("360Desktop.exe"); const CString PRO_PATH = _T("test_restart.exe");//_T("..\\..\\Bin\\Release\\360Desktop.exe"); const CString INI_PATH = _T("restart.ini");
2.2 重启一个程序,需要用到关闭一个进程、一个启动程序的程序(相关头文件里的声明以下均省略):
//启动进程 void CrestartDlg::CreateProPath(CString sProPath) { USES_CONVERSION; LPCSTR lpcs = NULL; lpcs = T2A(sProPath.GetBuffer(sProPath.GetLength())); WinExec(lpcs, SW_SHOWNORMAL); } //关闭进程 BOOL CrestartDlg::KillProName(CString sProName) { //创建进程快照(TH32CS_SNAPPROCESS表示创建所有进程的快照) HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0); //PROCESSENTRY32进程快照的结构体 PROCESSENTRY32 pe; //实例化后使用Process32First获取第一个快照的进程前必做的初始化操作 pe.dwSize = sizeof(PROCESSENTRY32); //下面的IF效果同: //if(hProcessSnap == INVALID_HANDLE_VALUE) 无效的句柄 if(!Process32First(hSnapShot,&pe)) { return FALSE; } //将字符串转换为小写 sProName.MakeLower(); //如果句柄有效 则一直获取下一个句柄循环下去 while (Process32Next(hSnapShot,&pe)) { //pe.szExeFile获取当前进程的可执行文件名称 CString scTmp = pe.szExeFile; //将可执行文件名称所有英文字母修改为小写 scTmp.MakeLower(); //比较当前进程的可执行文件名称和传递进来的文件名称是否相同 //相同的话Compare返回0 if(!scTmp.Compare(sProName)) { //从快照进程中获取该进程的PID(即任务管理器中的PID) DWORD dwProcessID = pe.th32ProcessID; HANDLE hProcess = ::OpenProcess(PROCESS_TERMINATE,FALSE,dwProcessID); ::TerminateProcess(hProcess,0); CloseHandle(hProcess); return TRUE; } scTmp.ReleaseBuffer(); } sProName.ReleaseBuffer(); return FALSE; }
2.3 对ini文件的操作,以及时间戳的操作,需要用到以下方法:
//时间戳比较time1 - time2 int CrestartDlg::CheckTime(int time1,int time2) { return (time1%100 + time1/100*60) - (time2%100 + time2/100*60); } //获取程序路径 CString CrestartDlg::GetExPath() { TCHAR exeFullPath[MAX_PATH]; // MAX_PATH GetModuleFileName(NULL,exeFullPath,MAX_PATH);//得到程序模块名称,全路径 CString strdir,tmpdir=exeFullPath; strdir=tmpdir.Left(tmpdir.ReverseFind('\\')); return strdir; } //获取当前系统时间 CString CrestartDlg::GetSysTime() { CTime tm; tm = CTime::GetCurrentTime(); CString sTime; sTime = tm.Format("%H%M%S"); //%Y%m%d return sTime; } //初始化ini BOOL CrestartDlg::InitINI(CString sINIPath) { CString sPath = this->GetExPath() + L"\\" + sINIPath; //ini文件全路径 CFileFind finder; BOOL find = finder.FindFile(sPath); if(!find) { this->WriteINI(sINIPath,L"TIME1",this->GetSysTime()); } return find; } //写入ini节点 void CrestartDlg::WriteINI(CString sINIPath,CString key,CString val) { CString sPath = this->GetExPath() + L"\\" + sINIPath; //ini文件全路径 ::WritePrivateProfileStringW(_T("360Desktop Restart"),key,val,sPath); } //读取ini节点 CString CrestartDlg::ReadINI(CString sINIPath,CString key) { CString val; CString sPath = GetExPath() + L"\\" + sINIPath; //ini文件全路径 ::GetPrivateProfileStringW(_T("360Desktop Restart"),key,_T("-1"),val.GetBuffer(MAX_PATH),MAX_PATH,sPath); val.ReleaseBuffer(); return val; }
2.4 需要用到的方法准备就绪,下面完成一个重启程序流程(本例将尝试在30秒内重启异常2次,读者应视具体情况自定流程):
//判断重启 void CrestartDlg::CheckRestart() { if(this->InitINI(INI_PATH)) //初始化ini { CString stNow = this->GetSysTime(); int ntNow = _ttoi(stNow); int ntPre1 = _ttoi(this->ReadINI(INI_PATH,L"TIME1")); if(ntPre1>0) { if(this->CheckTime(ntNow,ntPre1) < 30) { //重启间隔短 int ntPre2 = _ttoi(this->ReadINI(INI_PATH,L"TIME2")); if(ntPre2>0) { //第三次重启 if(this->CheckTime(ntPre2,ntPre1) < 30) { //重启间隔短 //补救失败,不处理 //... this->KillProName(PRO_NAME); } else { //重启间隔长,重新计数 this->KillProName(PRO_NAME); this->CreateProPath(PRO_PATH); } this->WriteINI(INI_PATH,L"TIME1",L"-1"); this->WriteINI(INI_PATH,L"TIME2",L"-1"); } else { //第二次重启 this->WriteINI(INI_PATH,L"TIME2",stNow); //第一次补救 //删除database //DeleteFile(L"dabase.db"); this->KillProName(PRO_NAME); this->CreateProPath(PRO_PATH); } } else { //重启间隔长,重新计数 this->WriteINI(INI_PATH,L"TIME1",L"-1"); this->WriteINI(INI_PATH,L"TIME2",L"-1"); this->KillProName(PRO_NAME); this->CreateProPath(PRO_PATH); } } else { //第一次重启 this->WriteINI(INI_PATH,L"TIME1",stNow); this->KillProName(PRO_NAME); this->CreateProPath(PRO_PATH); } } else { //没有ini文件,第一次重启 this->KillProName(PRO_NAME); //结束进程 this->CreateProPath(PRO_PATH); //启动进程 } }
2.5 最后在该dialog的OnInitDialog初始化中调用一下自定义的重启流程:
BOOL CrestartDlg::OnInitDialog() { CDialog::OnInitDialog(); #pragma region // 将“关于...”菜单项添加到系统菜单中。 // IDM_ABOUTBOX 必须在系统命令范围内。 ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE); if (pSysMenu != NULL) { CString strAboutMenu; strAboutMenu.LoadString(IDS_ABOUTBOX); if (!strAboutMenu.IsEmpty()) { pSysMenu->AppendMenu(MF_SEPARATOR); pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); } } // 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动 // 执行此操作 SetIcon(m_hIcon, TRUE); // 设置大图标 SetIcon(m_hIcon, FALSE); // 设置小图标 #pragma endregion // TODO: 在此添加额外的初始化代码 this->CheckRestart();//重启流程 this->OnOK();//关闭自己 return TRUE; // 除非将焦点设置到控件,否则返回 TRUE }
2.6 编译运行,本例结构如下:
相关文章推荐
- 【异常处理】MFC程序崩溃的友好处理
- Qt程式异常崩溃处理技巧(Win)
- Android中处理崩溃异常
- android 中处理崩溃异常并重启程序
- Android中处理崩溃异常
- Android中处理崩溃异常
- Qt程式异常崩溃处理技巧(Win)
- Android中处理崩溃异常
- Android全局异常处理(捕获异常,不弹出程序崩溃)
- MFC中关于m_pMainWnd = &dlg;处报错的解决方案,针对XX处未处理的异常:0xC0000005:写入位置0x00000020时发生访问冲突
- android 中处理崩溃异常
- Android中处理崩溃异常
- iOS崩溃 捕获异常处理
- win32/mfc 异常处理与总结
- Android中处理崩溃异常
- Android 如何处理崩溃的异常
- Android:处理程序崩溃异常
- android 中处理崩溃异常并重启程序
- Android中处理崩溃异常
- MFC异常处理和诊断