程序崩溃时抓取dump文件

当程序崩溃时,我们需要获取发生异常时的线程上下文信息,一般做法是调用SetUnhandledExceptionFilter函数设置程序崩溃时的处理函数,处理函数所做的工作为调用函数MiniDumpWriteDump生成dump文件以供调试,以下为简单的代码示例
#include <DbgHelp.h>
#pragma comment(lib, "dbghelp.lib")

class CExceptionHandler
{
public:
	static CString GetDumpFilename()
	{
		CString filename;
		TCHAR buffer[MAX_PATH];
		GetModuleFileName(NULL,buffer,MAX_PATH);
		filename = buffer;
		int index = filename.ReverseFind(_T('.'));
		filename=filename.Left(index);
		filename+=_T(".dmp");
		return filename;
	}

	static LONG WINAPI SelfUnhandledExceptionFilter(__in struct _EXCEPTION_POINTERS *ExceptionInfo)
	{
		USES_CONVERSION;

		MINIDUMP_EXCEPTION_INFORMATION ExceptionParam;
		ExceptionParam.ThreadId=GetCurrentThreadId();
		ExceptionParam.ExceptionPointers=ExceptionInfo;
		ExceptionParam.ClientPointers=TRUE;

		MINIDUMP_USER_STREAM UserStreams[2];

		WCHAR strBulid[MAX_PATH];
		StringCchPrintf(strBulid, MAX_PATH, L"Build: %s %s", A2W(__DATE__), A2W(__TIME__));
		UserStreams[0].Type=CommentStreamW;
		UserStreams[0].Buffer=strBulid;
		UserStreams[0].BufferSize=MAX_PATH;


		HMODULE hModule;
		GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
			(LPCTSTR)ExceptionInfo->ExceptionRecord->ExceptionAddress,&hModule);
		WCHAR strModuleName[MAX_PATH];
		GetModuleFileNameW(hModule,strModuleName,MAX_PATH);

		WCHAR szError[MAX_PATH*2];
		StringCchPrintf(szError,MAX_PATH*2,
			L"Module=%s ,Code=%x ,Flag=%x ,Address=%p",
			strModuleName,ExceptionInfo->ExceptionRecord->ExceptionCode,
			ExceptionInfo->ExceptionRecord->ExceptionFlags,
			ExceptionInfo->ExceptionRecord->ExceptionAddress);
		UserStreams[1].Type=CommentStreamW;
		UserStreams[1].Buffer=szError;
		UserStreams[1].BufferSize=MAX_PATH*2;


		MINIDUMP_USER_STREAM_INFORMATION UserStreamInfo;
		UserStreamInfo.UserStreamArray=UserStreams;
		UserStreamInfo.UserStreamCount=2;

		CString filename = GetDumpFilename();
		HANDLE hFile = CreateFile(filename, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);

		::MiniDumpWriteDump(GetCurrentProcess(), 
			GetCurrentProcessId(), 
			hFile, 
			MiniDumpWithFullMemory,
			&ExceptionParam,
			&UserStreamInfo,
			NULL
			);

		return EXCEPTION_EXECUTE_HANDLER;
	}

	static void Setup()
	{
		::SetUnhandledExceptionFilter(SelfUnhandledExceptionFilter);

	}
};


调用CExceptionHandler::Setup()即可处理捕获未处理的异常信息。

当我测试,发现CRT安全字符串函数wcsncpy_s出现异常并未产生dump文件,而是显示程序错误对话框,经查验得知,wcsncpy_s出现异常并不会调用SetUnhandledExceptionFilter设置的异常处理函数,而是调用_set_invalid_parameter_handler设置的无效参数处理函数,函数原型为

void __cdecl invalid_parameter_handler(const wchar_t * expression, const wchar_t *function, const wchar_t * file, unsigned int line, uintptr_t);

如要忽略CRT无效参数这种错误,可直接定义如下函数并且调用_set_invalid_parameter_handler注册

void __cdecl invalid_parameter_handler(const wchar_t * expression, const wchar_t *function, const wchar_t * file, unsigned int line, uintptr_t)
{
}

在调试版本中,无效参数回调函数的参数是存在有效值的,而在Release版本中,参数的值为NULL。

 

若要求出现无效参数异常时也要产生dump文件,可以在回调函数内部直接调用RaiseException函数,完整示例代码如下

#include "stdafx.h"
#include <iostream>
#include <atlbase.h>
#include <atlstr.h>
#include "Dumper.h"
#include <strsafe.h>
using namespace std;

void __cdecl invalid_parameter_handler(const wchar_t * expression, const wchar_t *function, const wchar_t * file, unsigned int line, uintptr_t)
{
	RaiseException(1,1,0,NULL);
}

void TriggerException()
{
	WCHAR buffer[5];
	wcsncpy_s(buffer,5,L"123456789",5);
}


int _tmain(int argc, _TCHAR* argv[])
{
	CDumper::GetInstance().Initialize();
	CDumper::GetInstance().Setup();
	_set_invalid_parameter_handler(invalid_parameter_handler);
	TriggerException();
	return 0;
}


请注意代码

wcsncpy_s(buffer,5,L"123456789",5);

就是设定了最后一个参数不大于目标字符串的长度,依然会产生无效参数异常,个人认为CRT字符串函数这样处理不友好,应该使用windows安全字符串函数系列(StringCbXXX 、StringCchXXX)。

 

我发现了一篇介绍的比我更全更好的文章。下一篇博客将转载它。

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值