Windows下程序崩溃生成dump文件的方法

本文介绍了Windows客户端应用在Release模式下遇到崩溃时,如何通过dump文件进行调试。首先解释了dump文件的重要性,然后提供了一个CrashDump类的实现,用于在程序运行时自动生成dump文件。该类在程序异常时调用UnhandledExceptionFilter函数,生成包含详细信息的minidump文件,便于后期分析崩溃原因。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一,为什么需要dump文件

    Windows客户端应用开发时,难免会遇到程序崩溃问题。当程序在Debug下运行崩溃时,我们可以直接定位到崩溃点。但是当程序打包成Release发布时,难免会遇到一些崩溃问题。一般遇到这样的崩溃,我们就需要使用 dump 文件加上符号表文件来进行调试程序。

二,如何生成dump文件

工欲善其事,必先利其器。这里直接给出一个CrashDump类,供各位大佬使用。在main函数实例化即可。生成不了dump文件你来打我~

三,CrashDump代码

CrashDump.h

#pragma once

#include <Windows.h>
#include <Dbghelp.h>

class CrashDump {
public:
    explicit CrashDump();
    ~CrashDump();
private:
    static LONG WINAPI UnhandledExceptionFilter(struct _EXCEPTION_POINTERS* pExceptionInfo);
private:
    LPTOP_LEVEL_EXCEPTION_FILTER        m_oldExceptionFilter;
};

CrashDump.cpp

#include "CrashDump.h"
#include <ctime>
#include <string>
#include <sstream>
#include <assert.h>
#include <DbgHelp.h>
#include <shellapi.h>
#include <iostream>

typedef BOOL(WINAPI *getUserModeExceptionProc)(LPDWORD);
typedef BOOL(WINAPI *setUserModeExceptionProc)(DWORD);

typedef BOOL(WINAPI *MINIDUMPWRITEDUMP) (
    HANDLE hProcess,
    DWORD ProcessId,
    HANDLE hFile,
    MINIDUMP_TYPE DumpType,
    PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
    PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
    PMINIDUMP_CALLBACK_INFORMATION CallbackParam
    );

static std::wstring format(const wchar_t* pszFormat, ...) {
    wchar_t buffer[MAX_PATH] = { 0 };

    va_list ap;
    va_start(ap, pszFormat);
    int nCount = ::vswprintf_s(buffer, _countof(buffer), pszFormat, ap);
    va_end(ap);

    if (nCount < 0) {
        assert(false);
        return pszFormat;
    }

    return buffer;
}

CrashDump::CrashDump()
    : m_oldExceptionFilter(NULL) {
    // 堆异常
    BOOL bRet = ::HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
    ::OutputDebugStringW(format(L"HeapSetInformation, bRet: <%lu, %lu>.\n", bRet, ::GetLastError()).c_str());

    // DEP策略
    bRet = ::SetProcessDEPPolicy(PROCESS_DEP_ENABLE | PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION);
    ::OutputDebugStringW(format(L"SetProcessDEPPolicy, bRet: <%lu, %lu>.\n", bRet, ::GetLastError()).c_str());

    //standard app-wide unhandled exception filter
    m_oldExceptionFilter = ::SetUnhandledExceptionFilter(UnhandledExceptionFilter);

    //fix for exceptions being swallowed inside callbacks (see KB976038)
    HMODULE hKernel32 = GetModuleHandle(TEXT("KERNEL32"));
    if (NULL == hKernel32) {
        ::OutputDebugStringW(L"GetModuleHandle faled.\n");
    } else {
        DWORD dwFlags = 0;
        getUserModeExceptionProc procGetProcessUserModeExceptionPolicy;
        setUserModeExceptionProc procSetProcessUserModeExceptionPolicy;

        procGetProcessUserModeExceptionPolicy = (getUserModeExceptionProc)::GetProcAddress(hKernel32, "GetProcessUserModeExceptionPolicy");
        procSetProcessUserModeExceptionPolicy = (setUserModeExceptionProc)::GetProcAddress(hKernel32, "SetProcessUserModeExceptionPolicy");

        if (procGetProcessUserModeExceptionPolicy && procSetProcessUserModeExceptionPolicy) {
            if (procGetProcessUserModeExceptionPolicy(&dwFlags)) {
                bRet = procSetProcessUserModeExceptionPolicy(dwFlags & ~1);
                ::OutputDebugStringW(format(L"GetProcessUserModeExceptionPolicy, bRet: %lu.\n", bRet).c_str());
            }

            ::OutputDebugStringW(format(L"SetProcessUserModeExceptionPolicy, bRet: %lu, dwFlags: %lu.\n", bRet, dwFlags).c_str());
        }
    }
}

CrashDump::~CrashDump() {
    if (NULL != m_oldExceptionFilter) {
        ::SetUnhandledExceptionFilter(m_oldExceptionFilter);
    }
}

LONG WINAPI CrashDump::UnhandledExceptionFilter(struct _EXCEPTION_POINTERS* pExceptionInfo) {
    //always break into a debugger if one is present
    if (::IsDebuggerPresent()) {
        ::OutputDebugStringW(L"IsDebuggerPresent return TRUE.\n");
        return EXCEPTION_CONTINUE_SEARCH;
    }

    static BOOL inExceptionHandler = FALSE;
    if (inExceptionHandler) {
        ::OutputDebugStringW(L"Current function has crashed.Shit.\n");
        return EXCEPTION_CONTINUE_SEARCH;
    }

    inExceptionHandler = TRUE;

    WCHAR fullPath[MAX_PATH] = { 0 };
    DWORD pathLength = ::GetModuleFileNameW(NULL, fullPath, MAX_PATH);
    if (0 == pathLength) {
        ::OutputDebugStringW(L"GetModuleFileNameW failed.\n");
        return EXCEPTION_CONTINUE_SEARCH;
    }

    LPCWSTR lastSlash = ::wcsrchr(fullPath, L'\\');
    if (NULL == lastSlash) {
        ::OutputDebugStringW(L"wcsrchr return wrong.\n");
        return EXCEPTION_CONTINUE_SEARCH;
    }

    std::wstring exeDirPath(fullPath, lastSlash - fullPath + 1);

    WCHAR filePath[MAX_PATH] = { 0 };
    for (int i = 0; ; ++i) { // 避免同名 
        SYSTEMTIME sys_time = { 0 };
        ::GetLocalTime(&sys_time);
        ::swprintf_s(filePath, _countof(filePath) - 1, L"%s%04u_%02u_%02u_%02u_%02u_%02u_%d.dmp"
            , exeDirPath.c_str()
            , sys_time.wYear, sys_time.wMonth, sys_time.wDay
            , sys_time.wHour, sys_time.wMinute, sys_time.wSecond, i);

        if (::GetFileAttributes(filePath) == INVALID_FILE_ATTRIBUTES) {
            break;
        }
    }

    HANDLE hFile = ::CreateFileW(filePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if (INVALID_HANDLE_VALUE == hFile) {
        ::OutputDebugStringW(L"CreateFileW failed.\n");
        return EXCEPTION_CONTINUE_SEARCH;
    }

    //load dbghelp dynamically
    HMODULE hDbgHelp = LoadLibraryW(L"DBGHELP");
    if (!hDbgHelp) {
        ::OutputDebugStringW(L"LoadLibraryW DBGHELP failed.\n");
        return EXCEPTION_CONTINUE_SEARCH;
    }

    MINIDUMPWRITEDUMP fnMiniDumpWriteDump = (MINIDUMPWRITEDUMP)::GetProcAddress(hDbgHelp, "MiniDumpWriteDump");
    if (!fnMiniDumpWriteDump) {
        ::OutputDebugStringW(L"GetProcAddress MiniDumpWriteDump failed.\n");
        ::FreeLibrary(hDbgHelp);
        return EXCEPTION_CONTINUE_SEARCH;
    }

    MINIDUMP_TYPE dumpFlags = (MINIDUMP_TYPE)(MiniDumpWithIndirectlyReferencedMemory | MiniDumpWithUnloadedModules | MiniDumpWithProcessThreadData);

    MINIDUMP_EXCEPTION_INFORMATION miniInfo = {0};
    miniInfo.ClientPointers = TRUE;
    miniInfo.ExceptionPointers = pExceptionInfo;
    miniInfo.ThreadId = ::GetCurrentThreadId();

    //generate a minidump if possible
    if (fnMiniDumpWriteDump(::GetCurrentProcess(), ::GetCurrentProcessId(), hFile, dumpFlags, &miniInfo, NULL, NULL)) {
        WCHAR buffer[MAX_PATH] = { 0 };	
        ::swprintf_s(buffer, _countof(buffer) - 1, L"Process has crashed.\nMinidump was saved to: \n\\%s\n", filePath);
        ::OutputDebugStringW(buffer);
        // ::MessageBoxW(NULL, buffer, NULL, MB_ICONERROR | MB_OK);
    } else {
        ::OutputDebugStringW(format(L"Minidump was saved failed: %hu.\n", ::GetLastError()).c_str());
        // ::MessageBoxW(NULL, format(L"Minidump was saved failed: %hu.\n", ::GetLastError()).c_str(), NULL, MB_ICONERROR | MB_OK);
    }

    ::FreeLibrary(hDbgHelp);
    ::CloseHandle(hFile);
    return EXCEPTION_CONTINUE_SEARCH;
}

四,在项目中调用

int main(int argc, char* argv[]) {
...
  CrashDump crashDump;  // 直接实例化即可
...
}

注意要用exe启动,不要用vs启动程序!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值