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:TCHARm_szMiniDumpPath[MAX_PATH];TCHARm_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.htypedef 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( PVOIDpParam,const PMINIDUMP_CALLBACK_INPUT pInput,PMINIDUMP_CALLBACK_OUTPUTpOutput){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(){HMODULEhModule = LoadLibrary(L"kernel32.dll");void* pAddr = (void*)GetProcAddress(hModule, "SetUnhandledExceptionFilter");if (pAddr){unsigned char code[16] = { 0 };intsize= 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;}elsereturn 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){BOOLbOk = FALSE;TOKEN_PRIVILEGEStp;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){LONGretval= EXCEPTION_CONTINUE_SEARCH;HANDLEhImpersonationToken = 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 };TCHARszDbgHelpPath[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 MiniDumpWriteDumpTOKEN_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 doneif (bPrivilegeEnabled)RestorePrivilege(hImpersonationToken, &tp);if (bOk){retval = EXCEPTION_EXECUTE_HANDLER;}::CloseHandle(hFile);}}}FreeLibrary(hDll);if (NULL != pExceptionInfo){TerminateProcess(GetCurrentProcess(), 0);}return retval;}测试

时间慢慢的流淌,人生有风雨阳光,

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

相关文章:

你感兴趣的文章:

标签云: