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、最小副作用,可长期运行
相关推荐
m0_380743876 分钟前
Qt实现调用相机进行拍照并进行图像处理
开发语言·c++
励志不掉头发的内向程序员2 小时前
【从零写一个CAD 03】三个 double 值得单独一个类吗:把视图变换抽成 View
开发语言·c++·qt·学习·系统架构
蒸蒸yyyyzwd9 小时前
秋招学习笔记 day46
c++·八股
段一凡-华北理工大学9 小时前
大模型应用开发 100 天:Python + LLM 从入门到精通 day26~Prompt 调试与优化——A/B 测试与效果评估
windows·python·大模型·prompt·智能体·提示词工程·高炉智能化
fox_charon11 小时前
Windows 下 CLI 参数的引号陷阱:为什么 --resume 'uuid' 会失败
windows·shell·cmd·cli·引号
染指111012 小时前
128.Agent-LangChain核心组件-中间件-调用模型的时候
人工智能·windows·中间件·langchain·agent
码匠许师傅12 小时前
【C++三方组件】cpp-httplib:一个头文件起 HTTP 服务
开发语言·c++·http
Frag0ut13 小时前
Chrome与Chromium内核浏览器在Windows 11上的新特性全景解析
前端·chrome·windows·web安全·chromium·gemini ai·playready drm
无名猿13 小时前
new/delete 与 malloc/free:为什么绝对不能混用
c++·内存管理·现代c++·踩坑记录
数安旭说14 小时前
企业终端安全实战:Windows软件管控、正版检测、变更审计与私有软件库落地
windows·企业安全·软件需求·终端安全·软件管理·正版检测·终端审计