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、最小副作用,可长期运行
相关推荐
你的冰西瓜2 小时前
C++中的queue容器详解
开发语言·c++·stl
蜡笔小马2 小时前
Boost.Polygon 库概述:高效处理平面多边形几何的利器
c++·boost
super_lzb2 小时前
【技术实战系列】windows系统如何模拟tail命令效果
windows·tail命令·日志查看·windows tail·windowstail命令
liu****2 小时前
4.镜像仓库详解
c++·docker·云原生·容器·kubernetes·镜像\
梵刹古音2 小时前
【C++】多态
开发语言·c++
Maguyusi3 小时前
go 批量生成c++和lua proto文件
c++·golang·lua·protobuf
fengsen52113143 小时前
Windows操作系统部署Tomcat详细讲解
java·windows·tomcat
Full Stack Developme3 小时前
spring #{} 与 ${} 区别
windows·python·spring
shentuyu木木木(森)3 小时前
单调队列 & 单调栈
数据结构·c++·算法·单调栈·单调队列