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、最小副作用,可长期运行
相关推荐
樱木Plus1 天前
深拷贝(Deep Copy)和浅拷贝(Shallow Copy)
c++
blasit3 天前
笔记:Qt C++建立子线程做一个socket TCP常连接通信
c++·qt·tcp/ip
阿白的白日梦3 天前
winget基础管理---更新/修改源为国内源
windows
肆忆_4 天前
# 用 5 个问题学懂 C++ 虚函数(入门级)
c++
不想写代码的星星4 天前
虚函数表:C++ 多态背后的那个男人
c++
端平入洛6 天前
delete又未完全delete
c++
端平入洛7 天前
auto有时不auto
c++
埃博拉酱7 天前
VS Code Remote SSH 连接 Windows 服务器卡在"下载 VS Code 服务器":prcdn DNS 解析失败的诊断与 BITS 断点续传
windows·ssh·visual studio code
唐宋元明清21887 天前
.NET 本地Db数据库-技术方案选型
windows·c#
加号38 天前
windows系统下mysql多源数据库同步部署
数据库·windows·mysql