【原理】
      windows程序当遇到异常,没有try-catch或者try-catch也无法捕获到的异常时,程序就会自动退出,如果这时候没有dump文件的话,我们是没有得到任何程序退出的信息。在windows程序异常退出之前,会预先调用一个在程序中注册的异常处理回调函数(默认是没有设置),只要我们在这个回调函数中调用MiniDumpWriteDump函数就可以产生我们想要的dump文件。


【实现】


1.调用SetUnhandledExceptionFilter注册一个自定义的异常处理回调函数

SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);


2.CreateFile创建dump文件,调用MiniDumpWriteDump函数往dump文件写异常信息


【实现】


#pragma  once 

#include <windows.h>

class CMiniDumper
{
public:
static HRESULT CreateInstance();
static HRESULT ReleaseInstance();

public:
LONG WriteMiniDump(_EXCEPTION_POINTERS *pExceptionInfo);

private:
void SetMiniDumpFileName(void);
BOOL GetImpersonationToken(HANDLE* phToken);
BOOL EnablePrivilege(LPCTSTR pszPriv, HANDLE hToken, TOKEN_PRIVILEGES* ptpOld);
BOOL RestorePrivilege(HANDLE hToken, TOKEN_PRIVILEGES* ptpOld);

private:
CMiniDumper();
virtual ~CMiniDumper(void);

private:
TCHAR m_szMiniDumpPath[MAX_PATH];
TCHAR m_szAppPath[MAX_PATH];
};
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <assert.h>
#include <time.h>
#include <tchar.h>
#include <dbghelp.h>
#include "miniDump.h"

#ifdef UNICODE
#define _tcssprintf wsprintf
#define tcsplitpath _wsplitpath
#else
#define _tcssprintf sprintf
#define tcsplitpath _splitpath
#endif


//-----------------------------------------------------------------------------
// GLOBALs
//-----------------------------------------------------------------------------
CMiniDumper *gs_pMiniDumper = NULL;
LPCRITICAL_SECTION gs_pCriticalSection = NULL;

//-----------------------------------------------------------------------------
// APIs
//-----------------------------------------------------------------------------
// Based on dbghelp.h
typedef BOOL(WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess,
DWORD dwPid,
HANDLE hFile,
MINIDUMP_TYPE DumpType,
CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam);

BOOL IsDataSectionNeeded(const WCHAR* pModuleName)
{
if (pModuleName == 0)
{
return FALSE;
}

WCHAR szFileName[_MAX_FNAME] = {0};
_wsplitpath(pModuleName, NULL, NULL, szFileName, NULL);

if (_wcsicmp(szFileName, L"ntdll") == 0)
return TRUE;

return FALSE;
}

BOOL WINAPI 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);
}
return TRUE;
case IncludeModuleCallback:
case IncludeThreadCallback:
case ThreadCallback:
case ThreadExCallback:
return TRUE;
default:;
}

return FALSE;
}

//-----------------------------------------------------------------------------
// Name: unhandledExceptionHandler()
// Desc: Call-back filter function for unhandled exceptions
//-----------------------------------------------------------------------------
LONG WINAPI UnhandledExceptionHandler(_EXCEPTION_POINTERS *pExceptionInfo)
{
if (NULL == gs_pMiniDumper)
return EXCEPTION_CONTINUE_SEARCH;

return gs_pMiniDumper->WriteMiniDump(pExceptionInfo);
}

// 此函数一旦成功调用,之后对 SetUnhandledExceptionFilter 的调用将无效
void DisableSetUnhandledExceptionFilter()
{
HMODULE hModule = LoadLibrary(L"kernel32.dll");
void* pAddr = (void*)GetProcAddress(hModule, "SetUnhandledExceptionFilter");
if (pAddr)
{
unsigned char code[16] = { 0 };
int size = 0;

code[size++] = 0x33;
code[size++] = 0xC0;
code[size++] = 0xC2;
code[size++] = 0x04;
code[size++] = 0x00;

DWORD dwOldFlag = 0;
DWORD dwTempFlag = 0;

VirtualProtect(pAddr, size, PAGE_READWRITE, &dwOldFlag);
WriteProcessMemory(GetCurrentProcess(), pAddr, code, size, NULL);
VirtualProtect(pAddr, size, dwOldFlag, &dwTempFlag);
}
FreeLibrary(hModule);
}

//-----------------------------------------------------------------------------
// Name: CreateInstance()
// Desc: Instanse gs_pMiniDumper
//-----------------------------------------------------------------------------
HRESULT CMiniDumper::CreateInstance()
{
if (NULL == gs_pMiniDumper)
{
gs_pMiniDumper = new CMiniDumper();
}
if (NULL == gs_pCriticalSection)
{
gs_pCriticalSection = new CRITICAL_SECTION;
InitializeCriticalSection(gs_pCriticalSection);
}

return(S_OK);
}

//-----------------------------------------------------------------------------
// Name: ReleaseInstance()
// Desc: Release gs_pMiniDumper
//-----------------------------------------------------------------------------
HRESULT CMiniDumper::ReleaseInstance()
{
if (NULL != gs_pMiniDumper)
{
delete gs_pMiniDumper;
gs_pMiniDumper = NULL;
}
if (NULL != gs_pCriticalSection)
{
DeleteCriticalSection(gs_pCriticalSection);
gs_pCriticalSection = NULL;
}

return(S_OK);
}

//-----------------------------------------------------------------------------
// Name: CMiniDumper()
// Desc: Constructor
//-----------------------------------------------------------------------------
CMiniDumper::CMiniDumper()
{
// 使应用程序能够取代每个进程和线程的顶级异常处理程序
::SetUnhandledExceptionFilter(UnhandledExceptionHandler);
DisableSetUnhandledExceptionFilter();
}

//-----------------------------------------------------------------------------
// Name: ~CMiniDumper()
// Desc: Destructor
//-----------------------------------------------------------------------------
CMiniDumper::~CMiniDumper(void)
{

}

//-----------------------------------------------------------------------------
// Name: setMiniDumpFileName()
// Desc:
//-----------------------------------------------------------------------------
void CMiniDumper::SetMiniDumpFileName(void)
{
time_t currentTime;
time(¤tTime);

_tcssprintf(m_szMiniDumpPath, _T("%s.%ld.dmp"), m_szAppPath, currentTime);
}

//-----------------------------------------------------------------------------
// Name: getImpersonationToken()
// Desc: The method acts as a potential workaround for the fact that the
// current thread may not have a token assigned to it, and if not, the
// process token is received.
//-----------------------------------------------------------------------------
BOOL CMiniDumper::GetImpersonationToken(HANDLE* phToken)
{
*phToken = NULL;
if (!OpenThreadToken(GetCurrentThread(),
TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,
TRUE,
phToken))
{
if (GetLastError() == ERROR_NO_TOKEN)
{
// No impersonation token for the current thread is available.
// Let's go for the process token instead.
if (!OpenProcessToken(GetCurrentProcess(),
TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,
phToken))
return FALSE;
}
else
return FALSE;
}

return TRUE;
}

//-----------------------------------------------------------------------------
// Name: enablePrivilege()
// Desc: Since a MiniDump contains a lot of meta-data about the OS and
// application state at the time of the dump, it is a rather privileged
// operation. This means we need to set the SeDebugPrivilege to be able
// to call MiniDumpWriteDump.
//-----------------------------------------------------------------------------
BOOL CMiniDumper::EnablePrivilege(LPCTSTR pszPriv, HANDLE hToken, TOKEN_PRIVILEGES* ptpOld)
{
BOOL bOk = FALSE;
TOKEN_PRIVILEGES tp;

tp.PrivilegeCount = 1;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
bOk = LookupPrivilegeValue(0, pszPriv, &tp.Privileges[0].Luid);
if (bOk)
{
DWORD cbOld = sizeof(*ptpOld);
bOk = AdjustTokenPrivileges(hToken, FALSE, &tp, cbOld, ptpOld, &cbOld);
}

return (bOk && (ERROR_NOT_ALL_ASSIGNED != GetLastError()));
}

//-----------------------------------------------------------------------------
// Name: restorePrivilege()
// Desc:
//-----------------------------------------------------------------------------
BOOL CMiniDumper::RestorePrivilege(HANDLE hToken, TOKEN_PRIVILEGES* ptpOld)
{
BOOL bOk = AdjustTokenPrivileges(hToken, FALSE, ptpOld, 0, NULL, NULL);
return (bOk && (ERROR_NOT_ALL_ASSIGNED != GetLastError()));
}

//-----------------------------------------------------------------------------
// Name: writeMiniDump()
// Desc:
//-----------------------------------------------------------------------------
LONG CMiniDumper::WriteMiniDump(_EXCEPTION_POINTERS *pExceptionInfo)
{
LONG retval = EXCEPTION_CONTINUE_SEARCH;
HANDLE hImpersonationToken = NULL;

if (!GetImpersonationToken(&hImpersonationToken))
return FALSE;

// You have to find the right dbghelp.dll.
// Look next to the EXE first since the one in System32 might be old (Win2k)
HMODULE hDll = NULL;
if (GetModuleFileName(NULL, m_szAppPath, _MAX_PATH))
{
wchar_t szDir[_MAX_DIR] = { 0 };
TCHAR szDbgHelpPath[MAX_PATH] = { 0 };

_wsplitpath(m_szAppPath, NULL, szDir, NULL, NULL);
_tcscpy(szDbgHelpPath, szDir);
_tcscat(szDbgHelpPath, _T("DBGHELP.DLL"));

hDll = ::LoadLibrary(szDbgHelpPath);
}

if (hDll == NULL)
{
// If we haven't found it yet - try one more time.
hDll = ::LoadLibrary(_T("DBGHELP.DLL"));
}

if (hDll)
{
// Get the address of the MiniDumpWriteDump function, which writes
// user-mode mini-dump information to a specified file.
MINIDUMPWRITEDUMP MiniDumpWriteDump = (MINIDUMPWRITEDUMP)::GetProcAddress(hDll, "MiniDumpWriteDump");
if (MiniDumpWriteDump != NULL)
{
SetMiniDumpFileName();

// Create the mini-dump file...
HANDLE hFile = ::CreateFile(m_szMiniDumpPath,
GENERIC_WRITE,
FILE_SHARE_WRITE,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);

if (hFile != INVALID_HANDLE_VALUE)
{
_MINIDUMP_EXCEPTION_INFORMATION ExInfo;

ExInfo.ThreadId = ::GetCurrentThreadId();
ExInfo.ExceptionPointers = pExceptionInfo;
ExInfo.ClientPointers = NULL;

MINIDUMP_CALLBACK_INFORMATION mci;
mci.CallbackRoutine = (MINIDUMP_CALLBACK_ROUTINE)MiniDumpCallback;
mci.CallbackParam = 0;

// We need the SeDebugPrivilege to be able to run MiniDumpWriteDump
TOKEN_PRIVILEGES tp;
BOOL bPrivilegeEnabled = EnablePrivilege(SE_DEBUG_NAME, hImpersonationToken, &tp);
BOOL bOk;

// DBGHELP.dll is not thread-safe, so we need to restrict access...
EnterCriticalSection(gs_pCriticalSection);
{
// Write out the mini-dump data to the file...
bOk = MiniDumpWriteDump(GetCurrentProcess(),
GetCurrentProcessId(),
hFile,
MiniDumpNormal,
(NULL == pExceptionInfo) ? (NULL) : (&ExInfo),
NULL,
&mci);
}
LeaveCriticalSection(gs_pCriticalSection);

// Restore the privileges when done
if (bPrivilegeEnabled)
RestorePrivilege(hImpersonationToken, &tp);

if (bOk)
{
retval = EXCEPTION_EXECUTE_HANDLER;
}

::CloseHandle(hFile);
}
}
}

FreeLibrary(hDll);
if (NULL != pExceptionInfo)
{
TerminateProcess(GetCurrentProcess(), 0);
}

return retval;
}

测试


// Dumper.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
#include "miniDump.h"

extern CMiniDumper *gs_pMiniDumper;

int _tmain(int argc, _TCHAR* argv[])
{
CMiniDumper::CreateInstance();

char *p = NULL;
*p = 'b';

/*__try
{
*p = 'b';
}
__except (gs_pMiniDumper->WriteMiniDump(GetExceptionInformation()), EXCEPTION_CONTINUE_EXECUTION)
{

}*/
}

生成的dmp文件


C++ 记录Windows程序崩溃时的dumpfile_#include

sh