CDumpUtils.h
#pragma once
#include <string>
#include <tchar.h>
#include <windows.h>
#include <vector>
#include <map>
#ifdef _UNICODE
using _tstring = std::wstring;
#else
using _tstring = std::string;
#endif
class CDumpUtils
{
public:
//
// @brief: 安装Dump
// @param: strDir 存放目录
// @param: fFull 是否完全转储
// @param: nCount 内存转储文件数量(0: 无限制)
// @ret: void
static void Install(const _tstring& strDir = _T("Dump"), bool fFull = false, size_t nCount = 8);
private:
//
// @brief: 构造
// @param: strDir 存放目录
// @param: fFull 是否完全转储
// @param: nCount 内存转储文件数量(0: 无限制)
CDumpUtils(const _tstring & = _T("Dump"), bool fFull = false, size_t nCount = 8);
~CDumpUtils();
//
// @brief: 安装Dump
// @param: strDir 存放目录
// @param: fFull 是否完全转储
// @param: nCount 内存转储文件数量(0: 无限制)
// @ret: void
void _Install(const _tstring & = _T("Dump"), bool fFull = false, size_t nCount = 8);
//
// @brief: 获取当前进程名
// @ret: 当前进程名 如 HxDPortableSetup.exe
static _tstring GetCurrentModuleName(bool bHasExt = false);
//
// @brief: 获取当前进程完全路径
// @ret: 当前进程完全路径 如 D:\Software\HxDPortableSetup.exe
static _tstring GetCurrentModulePath();
//
// @brief: 获取文件名
// @param: strPath 文件名, 如: D:\Software\HxDPortableSetup.exe
// @param: bHasExt 是否包含扩展名
// @ret: 文件夹 如 HxDPortableSetup
static _tstring GetFileName(const _tstring& strPath, bool bHasExt = false);
//
// @brief: 获取当前进程所在目录
// @ret: 当前进程所在目录 如 D:\Software
static _tstring GetCurrentModuleDir();
//
// @brief: 获取文件所在文件夹
// @param: strPath 文件名, 如: D:\Software\HxDPortableSetup.exe
// @ret: 文件夹 如 D:\Software
static _tstring GetFileDir(const _tstring& strPath);
//
// @brief: 创建目录(递归)
// @param: strPath 路径
// @ret: 成功返回true
static bool CreateDir(const _tstring& strPath);
//
// @brief: 格式化字符串
// @param: void
// @ret: bool 执行结果
static _tstring Format(LPCTSTR pstrFormat, ...);
//
// @brief: 获取目录下文件路径
// @ret: std::vector<_tstring> 日志文件列表
static std::map<int64_t, _tstring> _GetDumpFileList(const _tstring& strDir);
//
// @brief: 异常处理回调
// @param: pExceptionInfo 异常信息
// @ret: LONG 处理结果
static LONG WINAPI UnhandledExceptionFilter(struct _EXCEPTION_POINTERS* pExceptionInfo);
private:
static CDumpUtils m_Instance;
_tstring m_strFileDir;
size_t m_nFileCount;
bool m_fFull;
};
CDumpUtils.cpp
#include "CDumpUtils.h"
#include <strsafe.h>
#include <Dbghelp.h>
#define DMP_FILE_SAVE_PATH_FORMAT _T(R"(%s\%s_%04d-%02d-%02d_%02d-%02d-%02d.dmp)")
#define DMP_FILE_NAME_FORMAT _T(R"(%s_%%04d-%%02d-%%02d_%%02d-%%02d-%%02d.dmp)")
#pragma comment(lib, "Dbghelp.lib")
CDumpUtils CDumpUtils::m_Instance;
CDumpUtils::CDumpUtils(const _tstring& strDir/* = _T("")*/, bool fFull/* = false*/, size_t nCount/* = 8*/)
:
m_strFileDir(strDir),
m_fFull(fFull),
m_nFileCount(nCount)
{
}
CDumpUtils::~CDumpUtils()
{
}
void CDumpUtils::Install(const _tstring& strDir/* = _T("Dump")*/, bool fFull/* = false*/, size_t nCount/* = 8*/)
{
m_Instance._Install(strDir, fFull, nCount);
}
void CDumpUtils::_Install(const _tstring& strDir/* = _T("Dump")*/, bool fFull/* = false*/, size_t nCount/* = 8*/)
{
m_strFileDir = strDir;
m_fFull = fFull;
m_nFileCount = nCount;
::SetUnhandledExceptionFilter(UnhandledExceptionFilter);
}
_tstring CDumpUtils::GetCurrentModulePath()
{
TCHAR szCurPath[MAX_PATH] = { 0 };
::GetModuleFileName(NULL, szCurPath, _countof(szCurPath));
_tstring strResult = szCurPath;
return strResult;
}
_tstring CDumpUtils::GetCurrentModuleName(bool bHasExt/* = true*/)
{
return GetFileName(GetCurrentModulePath(), bHasExt);
}
_tstring CDumpUtils::GetFileName(const _tstring& strPath, bool bHasExt/* = true*/)
{
_tstring strResult = strPath;
size_t nIndex = strResult.find_last_of(_T('\\'));
if (nIndex != _tstring::npos)
{
strResult = strResult.substr(nIndex + 1);
}
if (!bHasExt)
{
nIndex = strResult.find_last_of(_T('.'));
if (nIndex != _tstring::npos)
{
strResult.resize(nIndex);
}
}
return strResult;
}
_tstring CDumpUtils::GetCurrentModuleDir()
{
return GetFileDir(GetCurrentModulePath());
}
_tstring CDumpUtils::GetFileDir(const _tstring& strPath)
{
_tstring strResult;
size_t nIndex = strPath.find_last_of(_T('\\'));
if (nIndex != _tstring::npos)
{
strResult = strPath.substr(0, nIndex);
}
return strResult;
}
bool CDumpUtils::CreateDir(const _tstring& strPath)
{
_tstring strDriver; //驱动器号, 如 D:
_tstring strSubPath = strPath; //路径, 如 Test\1\2\3
if (strPath.empty())
{
return false;
}
//获取盘符
do
{
size_t nFindIndex = strPath.find_first_of(':'); //检查是否有驱动器号
if (nFindIndex == _tstring::npos)
{
break;
}
strDriver = strPath.substr(0, nFindIndex + 1); //得到驱动器号, 如 D:
nFindIndex = strPath.find(_T("\\"), nFindIndex);
if (nFindIndex == _tstring::npos)
{
break;
}
strSubPath = strPath.substr(nFindIndex + 1); //得到路径, 如 Test\1\2\3
} while (false);
_tstring strDestDir;
size_t nFindBegin = 0;
size_t nFindIndex = 0;
do
{
nFindIndex = strSubPath.find(_T("\\"), nFindBegin);
if (nFindIndex != _tstring::npos)
{
strDestDir = strSubPath.substr(0, nFindIndex);
nFindBegin = nFindIndex + 1;
}
else
{
strDestDir = strSubPath;
}
if (!strDriver.empty())
{
strDestDir = strDriver + _T("\\") + strDestDir;
}
if (!::CreateDirectory(strDestDir.c_str(), NULL) && ERROR_ALREADY_EXISTS != ::GetLastError())
{
return false;
}
} while (nFindIndex != _tstring::npos);
return true;
}
_tstring CDumpUtils::Format(LPCTSTR pstrFormat, ...)
{
_tstring strResult;
LPTSTR lpBuf = nullptr;
DWORD dwCchCount = MAX_PATH;
if (nullptr == pstrFormat)
{
return strResult;
}
va_list args;
va_start(args, pstrFormat);
do
{
//分配缓冲
lpBuf = (LPTSTR)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, dwCchCount * sizeof(TCHAR));
if (nullptr == lpBuf)
{
break;
}
//成功则赋值字符串并终止循环
if (-1 != _vsntprintf_s(lpBuf, dwCchCount, _TRUNCATE, pstrFormat, args))
{
strResult = lpBuf;
break;
}
//释放缓冲, 待下次重新分配
::HeapFree(::GetProcessHeap(), 0, lpBuf);
lpBuf = nullptr;
//缓冲字符数在合理范围内则扩大2倍
if (dwCchCount < INT32_MAX)
{
dwCchCount *= 2;
}
else
{
//超限终止处理
break;
}
} while (true);
va_end(args);
//释放缓冲
if (nullptr != lpBuf)
{
::HeapFree(::GetProcessHeap(), 0, lpBuf);
lpBuf = nullptr;
}
return strResult;
}
std::map<int64_t, _tstring> CDumpUtils::_GetDumpFileList(const _tstring& strDir)
{
_tstring strScanDir = strDir;
if (strScanDir.empty())
{
strScanDir = GetCurrentModuleDir();
}
std::map<int64_t, _tstring> fileList;
WIN32_FIND_DATA findData = { 0 };
HANDLE hFindHandle = INVALID_HANDLE_VALUE;
hFindHandle = FindFirstFile((strScanDir + _T("\\*.*")).c_str(), &findData);
if (INVALID_HANDLE_VALUE == hFindHandle)
{
return fileList;
}
do
{
_tstring strName = findData.cFileName;
//非目录
if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
{
//检查输入规则
int nConverted = 0;
SYSTEMTIME st = { 0 };
_tstring strPath = strName;
if (!strDir.empty())
{
strPath = strDir + _T("\\") + strName;
}
_tstring strPrefix = Format(DMP_FILE_NAME_FORMAT, GetCurrentModuleName().c_str());
nConverted = _stscanf_s(findData.cFileName, strPrefix.c_str(),
&st.wYear, &st.wMonth, &st.wDay, &st.wHour,
&st.wMinute, &st.wSecond, &st.wMilliseconds);
//检查文件名规则是否符合要求
if (6 == nConverted)
{
FILETIME ftFile = { 0 };
FILETIME ftLocal = { 0 };
int64_t timestamp = 0;
::SystemTimeToFileTime(&st, &ftLocal);
::LocalFileTimeToFileTime(&ftLocal, &ftFile);
timestamp = ((int64_t)ftFile.dwHighDateTime << 32) | ftFile.dwLowDateTime;
timestamp = (timestamp - 116444736000000000) / 10000;
fileList.insert(std::make_pair(timestamp, strPath));
}
}
//上一级目录与当前目录跳过
if (0 == _tcscmp(findData.cFileName, _T(".")) || 0 == _tcscmp(findData.cFileName, _T("..")))
{
continue;
}
} while (::FindNextFile(hFindHandle, &findData));
::FindClose(hFindHandle);
return fileList;
}
LONG WINAPI CDumpUtils::UnhandledExceptionFilter(struct _EXCEPTION_POINTERS* ExceptionInfo)
{
SYSTEMTIME st = { 0 };
(void)::GetLocalTime(&st);
CDumpUtils& dumper = m_Instance;
_tstring strDmpDir = dumper.m_strFileDir;
if (strDmpDir.empty())
{
strDmpDir = GetCurrentModuleDir();
}
// 创建保存文件夹
if (!CreateDir(strDmpDir))
{
//return EXCEPTION_EXECUTE_HANDLER;
}
_tstring strFilePath = Format(DMP_FILE_SAVE_PATH_FORMAT,
strDmpDir.c_str(),
GetCurrentModuleName().c_str(),
st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
// 创建文件
HANDLE lhDumpFile = ::CreateFile(
strFilePath.c_str(),
GENERIC_WRITE,
0,
nullptr,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if (INVALID_HANDLE_VALUE != lhDumpFile)
{
MINIDUMP_EXCEPTION_INFORMATION loExceptionInfo = { 0 };
loExceptionInfo.ExceptionPointers = ExceptionInfo;
loExceptionInfo.ThreadId = GetCurrentThreadId();
loExceptionInfo.ClientPointers = TRUE;
// 将内存转储写入到文件
::MiniDumpWriteDump(
GetCurrentProcess(),
GetCurrentProcessId(),
lhDumpFile,
dumper.m_fFull ? MiniDumpWithFullMemory : MiniDumpNormal,
&loExceptionInfo,
nullptr,
nullptr
);
::CloseHandle(lhDumpFile);
}
// 文件数量限定
if (dumper.m_nFileCount > 0)
{
std::map<int64_t, _tstring> dumpFileList = dumper._GetDumpFileList(strDmpDir);
if (dumpFileList.size() > dumper.m_nFileCount)
{
size_t nDeleteCount = dumpFileList.size() - dumper.m_nFileCount;
// 从日志文件记录列表中删除
for (size_t i = 0; i < nDeleteCount; i++)
{
auto itBegin = dumpFileList.begin();
::DeleteFile(itBegin->second.c_str());
dumpFileList.erase(dumpFileList.begin());
}
}
}
return EXCEPTION_EXECUTE_HANDLER;
}
main.cpp
#include <locale.h>
#include "Win32Utils/CDumpUtils.h"
int _tmain(int argc, LPCTSTR argv[])
{
setlocale(LC_ALL, "");
CDumpUtils::Install(_T("Dump"), true, 4);
int* p = 0;
_tprintf(_T("%d\n"), *p);
return 0;
}


文章介绍了CDumpUtils类,一个用于安装进程转储、获取文件路径、处理异常的C++类,包含了方法如安装转储文件、获取当前模块路径等实用功能。
1296

被折叠的 条评论
为什么被折叠?



