昨天看了同事的程序,发现解决崩溃弹窗问题的方法很不错,上网搜了一下,也基本都是这个方法,以前确实没怎么关注过这个问题,测试了一下,把出现问题的源代码准确的定位了出来。
1、同事对其进行了封装,封装类如下:
#pragma once
#include <windows.h>
#include <imagehlp.h>
#include <stdlib.h>
#pragma comment(lib, "dbghelp.lib")
#define _DUMP_FILE_PATH (_T("dump\\"))
class DumpFileManager
{
private:
//要生成的小转储文件文件名称
CString m_strDumpFile;
private:
//判断是否为需要的数据区域
BOOL IsDataSectionNeeded(const WCHAR* pModuleName);
//发生错误时的回调函数
static BOOL CALLBACK MiniDumpCallback(PVOID pParam,const PMINIDUMP_CALLBACK_INPUT pInput,PMINIDUMP_CALLBACK_OUTPUT pOutput);
//创建小转储文件
BOOL CreateMiniDump(EXCEPTION_POINTERS* pep, LPCTSTR strFileName);
//钩子函数
static LPTOP_LEVEL_EXCEPTION_FILTER WINAPI MyDummySetUnhandledExceptionFilter(LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter);
//钩子函数
BOOL PreventSetUnhandledExceptionFilter();
//回调函数
static LONG WINAPI UnhandledExceptionFilterEx(struct _EXCEPTION_POINTERS *pException);
public:
//指定小转储文件保存路径
BOOL DeclarDumpFile(CString strAppPath);
//注册异常处理回调函数
void RunCrashHandler();
};
#include "StdAfx.h"
#include "DumpFileManager.h"
DumpFileManager *g_dump_manager = NULL;
BOOL DumpFileManager::IsDataSectionNeeded(const WCHAR* pModuleName)
{
WCHAR szFileName[_MAX_FNAME] = L"";
if(NULL == pModuleName)
{
return FALSE;
}
_wsplitpath(pModuleName, NULL, NULL, szFileName, NULL);
if(_wcsicmp(szFileName, L"ntdll") == 0)
{
return TRUE;
}
return FALSE;
}
BOOL CALLBACK DumpFileManager::MiniDumpCallback(PVOID pParam,const PMINIDUMP_CALLBACK_INPUT pInput,PMINIDUMP_CALLBACK_OUTPUT pOutput)
{
if(pInput == 0 || pOutput == 0)
{
return FALSE;
}
switch(pInput->CallbackType)
{
case ModuleCallback:
if(pOutput->ModuleWriteFlags & ModuleWriteDataSeg)
{
if(!(g_dump_manager->IsDataSectionNeeded(pInput->Module.FullPath)))
{
pOutput->ModuleWriteFlags &= (~ModuleWriteDataSeg);
}
}
case IncludeModuleCallback:
case IncludeThreadCallback:
case ThreadCallback:
case ThreadExCallback:
return TRUE;
default:
break;
}
return FALSE;
}
BOOL DumpFileManager::CreateMiniDump(EXCEPTION_POINTERS* pep, LPCTSTR strFileName)
{
MINIDUMP_CALLBACK_INFORMATION mci;
MINIDUMP_EXCEPTION_INFORMATION mdei;
HANDLE hFile = NULL;
if(NULL == pep || NULL == strFileName)
{
return FALSE;
}
hFile = CreateFile(strFileName,GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE))
{
mdei.ThreadId = GetCurrentThreadId();
mdei.ExceptionPointers = pep;
mdei.ClientPointers = FALSE;
mci.CallbackRoutine = (MINIDUMP_CALLBACK_ROUTINE)MiniDumpCallback;
mci.CallbackParam = 0;
MINIDUMP_TYPE mdt = (MINIDUMP_TYPE)0x0000ffff;
MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpNormal, &mdei, NULL, &mci);
CloseHandle(hFile);
return TRUE;
}
return FALSE;
}
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI DumpFileManager::MyDummySetUnhandledExceptionFilter(LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter)
{
return NULL;
}
BOOL DumpFileManager::PreventSetUnhandledExceptionFilter()
{
DWORD dwOrgEntryAddr = 0;
HMODULE hKernel32 = NULL;
void *pOrgEntry = NULL;
unsigned char newJump[100] = {0};
void *pNewFunc = NULL;
DWORD dwNewEntryAddr = 0;
DWORD dwRelativeAddr = 0;
SIZE_T bytesWritten = 0;
if (NULL == (hKernel32 = LoadLibrary(_T("kernel32.dll"))))
{
return FALSE;
}
if(NULL == (pOrgEntry = GetProcAddress(hKernel32, "SetUnhandledExceptionFilter")))
{
return FALSE;
}
dwOrgEntryAddr = (DWORD) pOrgEntry;
dwOrgEntryAddr += 5; // add 5 for 5 op-codes for jmp far
pNewFunc = &MyDummySetUnhandledExceptionFilter;
dwNewEntryAddr = (DWORD) pNewFunc;
dwRelativeAddr = dwNewEntryAddr - dwOrgEntryAddr;
newJump[0] = 0xE9; // JMP absolute
memcpy(&newJump[1], &dwRelativeAddr, sizeof(pNewFunc));
return WriteProcessMemory(GetCurrentProcess(),pOrgEntry,newJump,sizeof(pNewFunc) + 1,&bytesWritten);
}
LONG WINAPI DumpFileManager::UnhandledExceptionFilterEx(struct _EXCEPTION_POINTERS *pException)
{
BOOL bRetVal = FALSE;
if(NULL == pException || NULL == g_dump_manager)
{
return EXCEPTION_CONTINUE_SEARCH;
}
if(g_dump_manager->CreateMiniDump(pException,g_dump_manager->m_strDumpFile))
{
FatalAppExit(-1,_T("程序错误,小转储文件创建成功"));
}
else
{
FatalAppExit(-1,_T("程序错误,小转储文件创建失败"));
}
return EXCEPTION_CONTINUE_SEARCH;
}
//运行异常处理
void DumpFileManager::RunCrashHandler()
{
g_dump_manager = this;
SetUnhandledExceptionFilter(UnhandledExceptionFilterEx);
PreventSetUnhandledExceptionFilter();
}
BOOL DumpFileManager::DeclarDumpFile(CString strAppPath)
{
SYSTEMTIME syt;
CString strDumpFileName = _T("");
int xFlag = 0;
CString strNeedDir = _T("");
if(strAppPath.GetLength() <= 0)
{
return FALSE;
}
GetLocalTime(&syt);
strDumpFileName.Format(_T("%s%s"),strAppPath,_DUMP_FILE_PATH);
do
{
xFlag = strDumpFileName.Find(_T("\\"),xFlag+1);
if(xFlag > 3)
{
strNeedDir = strDumpFileName.Left(xFlag);
if(PathIsDirectory(strNeedDir))
{
continue ;
}
if(!(CreateDirectory(strNeedDir,0)))
{
break;
}
}
}while(xFlag >= 0);
xFlag = syt.wHour*60 + syt.wMinute;
m_strDumpFile.Format(_T("%s%04d-%02d-%02d_%d.dmp"),strDumpFileName,syt.wYear,syt.wMonth,syt.wDay,xFlag);
return TRUE;
}
2、使用方法:在程序开始运行的时候设置dump文件生成目录,并注册异常回调函数:
int npos = 0;
CString strPath = _T("");
TCHAR strTemp[MAX_PATH] = {0};
TCHAR strAppPath[MAX_PATH] = {0};
if(GetModuleFileName(NULL,strTemp, MAX_PATH) > 0)
{
strPath = strTemp;
if((npos = strPath.ReverseFind('\\')) > 0)
{
_tcscpy_s(strAppPath,strPath.Left(npos+1));
}
}
m_DumpFileManager.DeclarDumpFile(strAppPath);
m_DumpFileManager.RunCrashHandler();
3、在release下进行配置debug选项:
C/C++选项---常规---调试信息格式---(设置程序数据库/Zi);
连接器选项---调试---生成调试信息---(设置为是);
C/C++选项---优化---禁用。
4、调试时最好 *.exe 、*.pdb、*.dump、dbghelp.dll放在同一目录下,*.exe 、*.pdb要是同次编译生成的。双击*.dump进行调试,选择“使用仅限本机进行调试”,进行问题定位。