Dmp内存转储操作(Win32, C++)

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

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;
}

fe92b430ec78472589fa1eccbfdf0d54.png

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值