Windows程序崩溃捕获dump文件

Windows上程序崩溃时需要分析崩溃原因,需要程序崩溃的dmp文件和对应版本的pdb,Dump文件的获取方式有两大类:

  1. 通过WER或外部工具来捕获生成。
  2. 通过在代码中预埋,崩溃时自动生成dump文件。

一、通过WER或外部工具获取dump文件

对于已经发布的程序,基本主要使用该方式,通过修改注册表让WER或借助工具来生成dump文件。

1、 使用 procdump.exe 来捕获崩溃

procdump.exe 可以在程序启动之前或程序启动之后来进行监视,并在程序崩溃的时候生成dmp文件,其作为一个命令行工具,主要参数用法如下:

  • -accepteula : 自动接受许可协议(第一次运行不弹窗)
  • -e : 捕获未处理的异常(崩溃)
  • -ma : 生成 Full Dump
  • -w : 等待进程启动(wait),常用于捕获一启动就崩溃的进程
  • 在最后可以指定程序崩溃生成dump文件的路径,如果不指定则会保存在procdump.exe所在目录下

测试崩溃程序名称为 Test.exe

1)、程序未启动之前启动监视

在cmd中切换到 procdump 所在路径,并执行如下命令。如果指定dump文件所在路径,需要确保路该径存在

# 这里Test.exe为所监视程序的名称
procdump64.exe -accepteula -e -ma -w Test.exe ".\CrashDumps"

2)、程序启动之后启动监视

有些程序不需要未启动之前就监视,此时可以去掉 -w 参数,简洁命令如下:


procdump64.exe -e -ma Test.exe

根据需要可以将其封装成一个脚本,同 procdump放在同一个目录下,直接执行脚本来简化操作。


2、使用注册表启用WER来生成dump文件

WER自动捕获指定程序崩溃生成的dump文件,并指定dump文件的生成路径,需要修改注册表来启用对应的设置,注册表如下,针对特定的测试程序,其中 DumpFolder 为生成dump文件的路径,要保证程序要写入的权限

Windows Registry Editor Version 5.00

; 针对 64 位进程 (或 32 位系统上的 32 位进程)
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps\Test.exe]
"DumpFolder"="D:\\CrashDumps"
"DumpCount"=dword:00000010
"DumpType"=dword:00000002

; 针对 64 位系统上的 32 位进程 (关键补充)
[HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\Windows Error Reporting\LocalDumps\Test.exe]
"DumpFolder"="D:\\CrashDumps"
"DumpCount"=dword:00000010
"DumpType"=dword:00000002

执行这个注册表文件之后,会看到注册表的 WER 项有对应的修改


二、代码中内置崩溃异常生成dump文件

在当前的源代码中直接预埋对应注册函数,当异常崩溃发生时,直接自动捕获这个异常,并在指定位置生成对应的dump文件,对应的代码如下:

#include <windows.h>
#include <dbghelp.h>
#include <iostream>
#include <shlobj.h> // 用于创建目录
#include <string>

/** *************************************************************/
// 如果不存在会递归创建
void CreateFullDirectory(std::wstring path) {
    size_t pos = 0;
    do {
        pos = path.find_first_of(L"\\/", pos + 1);
        CreateDirectory(path.substr(0, pos).c_str(), NULL);
    } while (pos != std::wstring::npos);
}

void CreateDumpFile(EXCEPTION_POINTERS* pException) {

    // 方案 A: 自动获取 %LOCALAPPDATA% 路径
    wchar_t path[MAX_PATH];
    ExpandEnvironmentStrings(L"%LOCALAPPDATA%\\TestCrash\\", path, MAX_PATH);

    // 方案 B: 指定路径
    // wcscpy_s(path, L"D:\\MyDebugDumps\\");

    // 1. 确保目标文件夹存在
    CreateFullDirectory(path);

    // 2. 构造带时间戳的文件名,避免覆盖
    SYSTEMTIME st;
    GetLocalTime(&st);
    wchar_t fileName[MAX_PATH];
    swprintf_s(fileName, L"%lsCrash_%04d%02d%02d_%02d%02d%02d.dmp",
        path, st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);

    // 3. 创建文件
    HANDLE hFile = CreateFile(fileName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

    if (hFile != INVALID_HANDLE_VALUE) {
        MINIDUMP_EXCEPTION_INFORMATION dumpInfo;
        dumpInfo.ThreadId = GetCurrentThreadId();
        dumpInfo.ExceptionPointers = pException;
        dumpInfo.ClientPointers = TRUE;

        BOOL bSuccess = MiniDumpWriteDump(
            GetCurrentProcess(),
            GetCurrentProcessId(),
            hFile,
            MiniDumpNormal,
            &dumpInfo,
            NULL,
            NULL
        );

        if (bSuccess) {
            std::wcout << L"Dump saved to: " << fileName << std::endl;
        }
        CloseHandle(hFile);
    }
}

LONG WINAPI MyUnhandledExceptionFilter(struct _EXCEPTION_POINTERS* ExceptionInfo) {
    CreateDumpFile(ExceptionInfo);
    return EXCEPTION_EXECUTE_HANDLER;
}

/** *************************************************************/

// 主函数或dll程序入口注册这个异常过滤器,异常崩溃产生的时候自动生成dump文件

int main()
{
    SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);
    
    /** crash code **/
}

这种方式在一些复杂场景下可能不会如期生成对应的dump文件,如C++ 标准库的 abort() 有时会绕过这个过滤器,或者多线程下的栈破坏问题,此时需要使用成熟的捕获崩溃的开源库

posted @ 2025-12-20 17:18  Jeffxue  阅读(8)  评论(0)    收藏  举报