Windows环境c++开发dump文件生成(优化方案)

之前的方案容易导致软件正常运行时崩溃:

markup 复制代码
真正的问题是这三点叠加:

1️⃣ Vectored Exception Handler 里直接抓 dump
2️⃣ 异常是 COM / RPC(0x8001010D)这种"运行时主动 RaiseException"
3️⃣ 在异常上下文里调用了 TerminateProcess

👉 这三点组合在一起,在 UI / STA / COM 线程里非常容易挂死

重新优化后:

CCreateDump.h

cpp 复制代码
#pragma once
#include <Windows.h>

class CrashHandler
{
public:
    static void Install();

private:
    static LONG CALLBACK VectoredHandler(PEXCEPTION_POINTERS ep);
    static LONG WINAPI UnhandledHandler(PEXCEPTION_POINTERS ep);
    static void WriteMiniDump(PEXCEPTION_POINTERS ep);

private:
    static volatile LONG s_dumpWritten;
};

cpp:

cpp 复制代码
#include "CCreateDump.h"
#include <DbgHelp.h>
#include <strsafe.h>

#pragma comment(lib, "Dbghelp.lib")

volatile LONG CrashHandler::s_dumpWritten = 0;

void CrashHandler::Install()
{
    // 1️⃣ 安装 VEH(只观察,不终结)
    AddVectoredExceptionHandler(1, VectoredHandler);

    // 2️⃣ 安装未捕获异常处理(真正 crash 入口)
    SetUnhandledExceptionFilter(UnhandledHandler);
}

// ------------------------------------------------------------
// Vectored Exception Handler
// ------------------------------------------------------------
LONG CALLBACK CrashHandler::VectoredHandler(PEXCEPTION_POINTERS ep)
{
    if (!ep || !ep->ExceptionRecord)
        return EXCEPTION_CONTINUE_SEARCH;

    DWORD code = ep->ExceptionRecord->ExceptionCode;

    // ❌ 官方明确不当作 crash 的异常
    switch (code)
    {
    case EXCEPTION_BREAKPOINT:
    case EXCEPTION_SINGLE_STEP:
    case 0xE06D7363: // C++ exception
    case 0x8001010D: // RPC_E_CANTCALLOUT_ININPUTSYNCCALL
        return EXCEPTION_CONTINUE_SEARCH;
    default:
        break;
    }

    // ⭐ 只观察,不做任何处理,让异常继续传播到 UnhandledHandler
    // 不在这里设置 s_dumpWritten,否则会导致 UnhandledHandler 中无法写入 dump

    return EXCEPTION_CONTINUE_SEARCH;
}

// ------------------------------------------------------------
// Unhandled Exception Filter
// ------------------------------------------------------------
LONG WINAPI CrashHandler::UnhandledHandler(PEXCEPTION_POINTERS ep)
{
    if (!ep || !ep->ExceptionRecord)
        return EXCEPTION_EXECUTE_HANDLER;

    // 确保 dump 只写一次
    if (InterlockedCompareExchange(&s_dumpWritten, 1, 0) == 0)
    {
        WriteMiniDump(ep);
    }

    // 强制终止进程,确保进程完全退出
    // 使用退出码 -1 表示异常崩溃
    TerminateProcess(GetCurrentProcess(), 0xC0000005);  // STATUS_ACCESS_VIOLATION

    // 如果 TerminateProcess 失败(极少情况),返回 EXECUTE_HANDLER 让系统处理
    return EXCEPTION_EXECUTE_HANDLER;
}

// ------------------------------------------------------------
// MiniDump writer(纯函数,无副作用)
// ------------------------------------------------------------
void CrashHandler::WriteMiniDump(PEXCEPTION_POINTERS ep)
{
    wchar_t modulePath[MAX_PATH] = {};
    GetModuleFileNameW(nullptr, modulePath, MAX_PATH);

    wchar_t* slash = wcsrchr(modulePath, L'\\');
    if (slash) *slash = L'\0';

    SYSTEMTIME st;
    GetLocalTime(&st);

    DWORD pid = GetCurrentProcessId();
    DWORD tid = GetCurrentThreadId();

    wchar_t dumpPath[MAX_PATH] = {};
    StringCchPrintfW(
        dumpPath,
        MAX_PATH,
        L"%s\\Crash-%04d%02d%02d-%02d%02d%02d-P%u-T%u.dmp",
        modulePath,
        st.wYear, st.wMonth, st.wDay,
        st.wHour, st.wMinute, st.wSecond,
        pid, tid);

    HANDLE hFile = CreateFileW(
        dumpPath,
        GENERIC_WRITE,
        0,
        nullptr,
        CREATE_ALWAYS,
        FILE_ATTRIBUTE_NORMAL,
        nullptr);

    if (hFile == INVALID_HANDLE_VALUE)
    {
        // 如果创建文件失败,尝试写入错误日志
        wchar_t logPath[MAX_PATH] = {};
        StringCchPrintfW(logPath, MAX_PATH, L"%s\\CrashDump_Error.log", modulePath);
        HANDLE hLog = CreateFileW(logPath, GENERIC_WRITE, FILE_SHARE_READ, nullptr,
                                   CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
        if (hLog != INVALID_HANDLE_VALUE)
        {
            char errorMsg[256];
            sprintf_s(errorMsg, "Failed to create dump file. Error code: %u\r\n", GetLastError());
            DWORD written;
            WriteFile(hLog, errorMsg, (DWORD)strlen(errorMsg), &written, nullptr);
            CloseHandle(hLog);
        }
        return;
    }

    MINIDUMP_EXCEPTION_INFORMATION mei = {};
    mei.ThreadId = tid;
    mei.ExceptionPointers = ep;
    mei.ClientPointers = FALSE;

    MINIDUMP_TYPE type = (MINIDUMP_TYPE)(
        MiniDumpWithThreadInfo |
        MiniDumpWithUnloadedModules |
        MiniDumpWithHandleData |
        MiniDumpWithIndirectlyReferencedMemory
        );

    BOOL result = MiniDumpWriteDump(
        GetCurrentProcess(),
        pid,
        hFile,
        type,
        &mei,
        nullptr,
        nullptr);

    // 确保数据刷新到磁盘
    if (result)
    {
        FlushFileBuffers(hFile);
    }
    else
    {
        // 如果写入失败,记录错误
        wchar_t logPath[MAX_PATH] = {};
        StringCchPrintfW(logPath, MAX_PATH, L"%s\\CrashDump_Error.log", modulePath);
        HANDLE hLog = CreateFileW(logPath, GENERIC_WRITE, FILE_SHARE_READ, nullptr,
                                   CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
        if (hLog != INVALID_HANDLE_VALUE)
        {
            char errorMsg[256];
            sprintf_s(errorMsg, "MiniDumpWriteDump failed. Error code: %u\r\n", GetLastError());
            DWORD written;
            WriteFile(hLog, errorMsg, (DWORD)strlen(errorMsg), &written, nullptr);
            CloseHandle(hLog);
        }
    }

    CloseHandle(hFile);
}

优化设计原则

markup 复制代码
这套实现遵循 Windows 内部一贯原则:

1、不在 VEH 里做重活

2、真正写 dump 的地方只在 UnhandledExceptionFilter

3、不依赖 TerminateProcess

4、允许进程"自然死亡"

5、兼容 COM / STA / UIAutomation

6、最小副作用,可长期运行
相关推荐
Yu_Lijing16 小时前
基于C++的《Head First设计模式》笔记——备忘录模式
c++·笔记·设计模式·备忘录模式
tankeven16 小时前
HJ152 取数游戏
c++·算法
汉克老师16 小时前
GESPC++三级考试语法知识(五、字符数组 )
c++·字符数组·gesp三级·gesp3级·字母大小写转换
深邃-16 小时前
数据结构-队列
c语言·数据结构·c++·算法·html5
Rhystt16 小时前
代码随想录算法训练营第六十天|多余的边?从基础到进阶!
开发语言·c++·算法·图论
羊小猪~~16 小时前
【QT】-- QMainWindow简介
开发语言·数据库·c++·后端·qt·前端框架·求职招聘
桌面运维家17 小时前
解决Windows 10打印机脱机:端口、驱动、网络故障排除
windows·stm32·单片机
2301_8101609517 小时前
C++中的策略模式进阶
开发语言·c++·算法
-Rane17 小时前
【C++】map和set
开发语言·c++
keep intensify17 小时前
单源最短路径
数据结构·c++·算法