#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)。
我发现了一篇介绍的比我更全更好的文章。下一篇博客将转载它。