// minidmp.h
#pragma once
#include <windows h="">
#include <imagehlp h="">
#include <stdlib h="">
#include <strsafe h="">
#pragma comment(lib, "dbghelp.lib")
inline BOOL IsDataSectionNeeded(const WCHAR *pModuleName)
{
if(pModuleName == 0)
{
return FALSE;
}
WCHAR szFileName[_MAX_FNAME] = L"";
_wsplitpath(pModuleName, NULL, NULL, szFileName, NULL);
if(wcsicmp(szFileName, L"ntdll") == 0)
return TRUE;
return FALSE;
}
inline BOOL CALLBACK 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(!IsDataSectionNeeded(pInput->Module.FullPath))
pOutput->ModuleWriteFlags &= (~ModuleWriteDataSeg);
break;
case IncludeModuleCallback:
case IncludeThreadCallback:
case ThreadCallback:
case ThreadExCallback:
return TRUE;
default:
break;
}
return FALSE;
}
//創建Dump文件
inline void CreateMiniDump(EXCEPTION_POINTERS *pep, LPCWSTR strFileName)
{
HANDLE hFile = CreateFileW(strFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE))
{
MINIDUMP_EXCEPTION_INFORMATION mdei;
mdei.ThreadId = GetCurrentThreadId();
mdei.ExceptionPointers = pep;
mdei.ClientPointers = FALSE;
MINIDUMP_CALLBACK_INFORMATION mci;
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);
}
}
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI MyDummySetUnhandledExceptionFilter(LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter)
{
return NULL;
}
BOOL PreventSetUnhandledExceptionFilter()
{
HMODULE hKernel32 = LoadLibrary(_T("kernel32.dll"));
if (hKernel32 == NULL)
return FALSE;
void *pOrgEntry = GetProcAddress(hKernel32, "SetUnhandledExceptionFilter");
if(pOrgEntry == NULL)
return FALSE;
unsigned char newJump[ 100 ];
DWORD dwOrgEntryAddr = (DWORD) pOrgEntry;
dwOrgEntryAddr += 5; // add 5 for 5 op-codes for jmp far
void *pNewFunc = &MyDummySetUnhandledExceptionFilter;
DWORD dwNewEntryAddr = (DWORD) pNewFunc;
DWORD dwRelativeAddr = dwNewEntryAddr - dwOrgEntryAddr;
newJump[ 0 ] = 0xE9; // JMP absolute
memcpy(&newJump[ 1 ], &dwRelativeAddr, sizeof(pNewFunc));
SIZE_T bytesWritten;
BOOL bRet = WriteProcessMemory(GetCurrentProcess(), pOrgEntry, newJump, sizeof(pNewFunc) + 1, &bytesWritten);
return bRet;
}
LONG WINAPI UnhandledExceptionFilterEx(struct _EXCEPTION_POINTERS *pException)
{
WCHAR szPath[MAX_PATH];
WCHAR szFileName[MAX_PATH];
WCHAR *szAppName = L"DumpFile";
HANDLE hDumpFile;
SYSTEMTIME stLocalTime;
MINIDUMP_EXCEPTION_INFORMATION ExpParam;
GetLocalTime( &stLocalTime );
//GetTempPath( dwBufferSize, szPath );
GetModuleFileNameW(NULL, szPath, MAX_PATH);
wchar_t *pstr = wcsrchr(szPath, '\\');
memset(pstr + 1, 0, 2);
StringCchPrintfW( szFileName, MAX_PATH, L"%s%s", szPath, szAppName );
CreateDirectoryW( szFileName, NULL );
StringCchPrintfW( szFileName, MAX_PATH, L"%s%s\\%04d%02d%02d-%02d%02d%02d.dmp", szPath, szAppName,
stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay,
stLocalTime.wHour, stLocalTime.wMinute, stLocalTime.wSecond);
CreateMiniDump(pException, szFileName);
// TODO: 這裏可以將dump文件通過郵件發送給開發者,也可以通過http發送給服務端,服務端管理dump文件
FatalAppExit(-1, _T("*** Unhandled Exception! ***"));
return EXCEPTION_CONTINUE_SEARCH;
}
//運行異常處理
void RunCrashHandler()
{
SetUnhandledExceptionFilter(UnhandledExceptionFilterEx);
PreventSetUnhandledExceptionFilter();
}
// Test.cpp : 定義控制檯應用程序的入口點。
//
#include "stdafx.h"
#include "minidmp.h"
void CrashTest()
{
strcpy(NULL,"adfadfg");
}
int _tmain(int argc, _TCHAR* argv[])
{
//設置異常處理回調函數
RunCrashHandler();
CrashTest();
getchar();
return 0;
}
核心就是這樣了,注意點:程序發佈時記得帶上dbghelp.dll,這個是系統dll,開發機上肯定都會有。剩下的就是把dump文件上傳到服務器或者直接使用郵件發到指定郵箱了。
有了dump文件,結合發佈版本的pdb文件以及源代碼就能很快定位到崩潰點了。