C++ 记录Windows程序崩溃时的dumpfile

【原理】

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文件

sh

时间: 2024-10-19 18:22:10

C++ 记录Windows程序崩溃时的dumpfile的相关文章

【转】程序崩溃时自动记录minidump的c++类

原帖:程序崩溃时自动记录minidump的c++类 封装了一个C++类,当程序意外崩溃的时候可以生成dump文件,以便确定错误原因. 头文件: //crash_dumper_w32.h #ifndef _CRASH_DUMPER_H_ #define _CRASH_DUMPER_H_ #include <windows.h> class CrashDumper { public: CrashDumper(); ~CrashDumper(); static bool _PlaceHolder()

当您的应用程序崩溃时,您希望知道所有一切信息

为何应该关心崩溃报告 设想一个用户安装了一个移动应用程序的情况.由于抱有很高的预期,该应用程序被频繁地使用,或许在前几个星期一天使用好几次.应用程序创建的数据以及用户投入该应用程序中的精力在不断积累.然后,突然之间,应用程序崩溃了.用户的数据现在被损坏或丢失.尽管等待了好几天,该应用程序仍未更新,这些问题仍未得到解决.用户为该应用程序提供了一星评价和批评性评论,然后放弃了该应用程序.即使以后某个时刻提供了修复程序,用户也不可能返回再使用该应用程序. 作为应用程序供应商,您投入了数月时间来设计和开

android在程序崩溃时Catch异常并处理

Android系统的"程序异常退出",给应用的用户体验造成不良影响.为了捕获应用运行时异常并给出友好提示,便可继承UncaughtExceptionHandler类来处理.通过Thread.setDefaultUncaughtExceptionHandler()方法将异常处理类设置到线程上即可. 写一个例子来理解. 1.新建项目,新建一个MyCatchException类,实现uncaughtExceptionHandler. //全部错误捕捉器 public class MyCatc

&lt;转&gt;关闭 程序崩溃时 windows 正在检查该问题的解决方案

本文转自:http://www.cnblogs.com/dabaopku/archive/2011/07/04/2097029.html 尤其是使用visual studio开发程序 ,自己特意throw一个exception,每次都被它先捕捉到,等上10秒种才轮到vs调试器,甚是烦人. 关闭这个鸡肋的功能很简单,可惜隐藏的太深. 控制面板=>操作中心=>维护右边的箭头,打开隐藏的子窗口=>检查问题报告解决方案 选择设置,选择上那个不推荐设置就行了. 终于可以立即崩溃 了.

gdb调用程序崩溃时的内存镜像core文件

/* * gcc gdb_test.c -o gdb_test -g * ./gdb_test * Segmentation fault (core dumped) * gdb gdb_test -c core.5521 * Core was generated by `gdb_test'. * Program terminated with signal 11, Segmentation fault. * #0 0x000000000040048b in main (argc=1, argv=

设置程序崩溃时产生 core 文件的配置

1 /* 不限制 core 文件的大小 */ 2 ulimit -c unlimited 3 4 /* 使用 pid 进行命名 */ 5 echo "1" > /proc/sys/kernel/core_uses_pid 6 7 /* 配置 core 文件存储路径,及名称的格式 */ 8 echo "~/corefile/core-%e-%p-%t" > /proc/sys/kernel/core_pattern 原文地址:https://www.cnb

编写的windows程序,崩溃时产生crash dump文件的办法

一.引言 dump文件是C++程序发生异常时,保存当时程序运行状态的文件,是调试异常程序重要的方法,所以程序崩溃时,除了日志文件,dump文件便成了我们查找错误的最后一根救命的稻草.windows程序产生dump文件和linux程序产生dump文件的方式不一样,linux默认是不让产生core dump文件,只要在用户自己的~/.bash_profile文件中增加 ulimit -S -c unlimited > /dev/null 2>&1 这样程序崩溃就可以产生可调试的core d

如何定位Release 版本中程序崩溃的位置 ---利用map文件 拦截windows崩溃函数

1       案例描述 作为Windows程序员,平时最担心见到的事情可能就是程序发生了崩溃(异常),这时Windows会提示该程序执行了非法操作,即将关闭.请与您的供应商联系.呵呵,这句微软的“名言”,恐怕是程序员最怕见也最常见的东西了. 在一个大型软件的测试过程中,初期出现程序崩溃似乎成了不可避免的事.其实测试中出现程序崩溃并不可怕,反而是测试的成功.作为开发的我们更需要关心的是程序中的哪个函数或哪一行导致了系统崩溃,这样才能有针对性的进行改正. 本文描述了自己总结的几种定位崩溃的办法.

让程序在崩溃时体面的退出之Dump文件

在我的那篇<让程序在崩溃时体面的退出之CallStack>中提供了一个在程序崩溃时得到CallStack的方法.但是要想得到CallStack,必须有pdb文件的支持.但是普通情况下,公布出去的程序都是Release版本号的,都不会附带pdb文件.那么我们怎么能在程序崩溃的时候找到出错的详细位置呢?这个时候就该Dump文件出场了!Dump文件是进程的内存镜像,能够把程序执行时的状态完整的保存下来.         要想在程序崩溃的时候创建Dump文件,就须要用到DbgHelp.dll中Wind