C++调试之创建Dump文件和调试Dump文件

在发布后程序中捕获崩溃和异常较麻烦,日志记录法有局限。C++可调用window API函数CreateFile()和MiniDumpWriteDump()记录程序崩溃时的Dump信息,结合源码工程和.pdb文件能快速定位崩溃位置,虽有不足,但能捕获大多数崩溃问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

如何在发布后程序中捕获程序的崩溃和异常往往是比较麻烦的事情,一般采用日志记录的方法来记录程序运行的每个流程,但是通常为了程序运行的性能,日志记录的方法只是记录程序运行的每个主要的处理流程,不能进行具体详细的记录,比如for 循环中的崩溃记录。C++语言中调用window API函数CreateFile()和MiniDumpWriteDump(),可以方便的记录程序崩溃时的Dump信息,并保持dump文件,根据dump文件对应的源码工程和.pdb文件,我们就可以快速的定位到程序崩溃的源码位置,极大的提高了调试代码的效率。然而,Dump记录程序崩溃的方法不是万能的,有时候一些数组越界、容器访问异常等致命的问题,也不一定能准确的记录下来。总之,Dump文件记录程序崩溃的方法是开放人员常用的方法,并且能捕获大多数的程序崩溃问题。

下面是一段记录程序崩溃时生成Dump文件的方法,只要在工程中包含这个头文件代码,然后在程序构造函数中声明    RunCrashHandler(); 即可在程序崩溃时记录生成的Dump文件:


#ifndef  DUMPFILE_H
#define DUMPFILE_H

#include <Windows.h>
#include <tchar.h>
#include <direct.h>  
#include <Dbghelp.h>
using namespace std;

#pragma comment(lib, "Dbghelp.lib")

namespace NSDumpFile
{
	void CreateDumpFile(LPCWSTR lpstrDumpFilePathName, EXCEPTION_POINTERS *pException)
	{
		// 创建Dump文件  
		HANDLE hDumpFile = CreateFile(lpstrDumpFilePathName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
		
		// Dump信息  
		MINIDUMP_EXCEPTION_INFORMATION dumpInfo;
		dumpInfo.ExceptionPointers = pException;
		dumpInfo.ThreadId = GetCurrentThreadId();
		dumpInfo.ClientPointers = TRUE;

		// 写入Dump文件内容  
		MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hDumpFile, MiniDumpNormal, &dumpInfo, NULL, NULL);
		CloseHandle(hDumpFile);
	}
	
	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)
	{
		//TCHAR szMbsFile[MAX_PATH] = { 0 };
		//::GetModuleFileName(NULL, szMbsFile, MAX_PATH);
		//TCHAR* pFind = _tcsrchr(szMbsFile, '\\');
		//if (pFind)
		//{
		//*(pFind + 1) = 0;
		//_tcscat_s(szMbsFile, _T("CrashDumpFile.dmp"));
		//}

		//创建 dmp 文件夹
		char *_Path = "dumps";
		int retval = _mkdir(_Path);
		TCHAR szFileName[MAX_PATH] = { 0 };
		TCHAR* szVersion = _T("Reg");

		SYSTEMTIME stLocalTime;
		GetLocalTime(&stLocalTime);
		wsprintf(szFileName, L"dumps/%s_%04d%02d%02d-%02d%02d%02d.dmp",
			szVersion, stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay,
			stLocalTime.wHour, stLocalTime.wMinute, stLocalTime.wSecond);
		CreateDumpFile(szFileName, pException);

		// TODO: MiniDumpWriteDump
		FatalAppExit(-1, _T("Fatal Error! Dump Create Success!"));
		return EXCEPTION_CONTINUE_SEARCH;
	}

	void RunCrashHandler()
	{
		SetUnhandledExceptionFilter(UnhandledExceptionFilterEx);
		PreventSetUnhandledExceptionFilter();
	}
};

#define DeclareDumpFile() NSDumpFile::RunCrashHandler();

#endif //DUMPFILE_H

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值