一、调试前置条件
1、在编译之后,exe和pdb文件均需要发布给用户;
二、抓取异常的系统函数区分
cpp
AddVectoredExceptionHandler和SetUnhandledExceptionFilter
AddVectoredExceptionHandler 是"第一现场观察者",
SetUnhandledExceptionFilter 是"最后兜底处理者"。
当一个异常发生时,Windows 内部顺序是:
``1️⃣ Vectored Exception Handlers (VEH)
↓
2️⃣ 线程的 SEH (__try / __except)
↓
3️⃣ C++ try / catch
↓
4️⃣ UnhandledExceptionFilter (UEF)
↓
5️⃣ Windows 默认异常处理(弹框 / 进程终止)`
| 对比项 | VEH | UEF |
|---|---|---|
| 调用时机 | 最早 | 最后 |
| 是否能看到 first-chance | ✅ 能 | ❌ 不能 |
| 是否会被覆盖 | ❌ 不会 | ✅ 经常 |
| 稳定性 | ⭐⭐⭐⭐⭐ | ⭐⭐ |
| 使用难度 | 高 | 低 |
| 线上推荐 | ✅ | ❌ |
三、可直接复用的代码:
CrashDump.h:
cpp
#pragma once
#include <Windows.h>
class CrashDump
{
public:
static void Install();
private:
static LONG CALLBACK VectoredHandler(PEXCEPTION_POINTERS ep);
static void WriteDump(PEXCEPTION_POINTERS ep);
private:
static volatile LONG s_dumped;
};
CrashDump.cpp
cpp
#include "CrashDump.h"
#include <DbgHelp.h>
#include <strsafe.h>
#pragma comment(lib, "Dbghelp.lib")
volatile LONG CrashDump::s_dumped = 0;
void CrashDump::Install()
{
AddVectoredExceptionHandler(1, VectoredHandler);
}
static bool IsFatalException(DWORD code, DWORD flags)
{
// 明确忽略的异常(正常流程)
switch (code)
{
case EXCEPTION_BREAKPOINT:
case EXCEPTION_SINGLE_STEP:
case 0xE06D7363: // C++ Exception
return false;
default:
break;
}
// 不可继续异常 → 一定是致命的
if (flags & EXCEPTION_NONCONTINUABLE)
return true;
// 常见致命异常
switch (code)
{
case EXCEPTION_ACCESS_VIOLATION:
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
case EXCEPTION_DATATYPE_MISALIGNMENT:
case EXCEPTION_ILLEGAL_INSTRUCTION:
case EXCEPTION_IN_PAGE_ERROR:
case EXCEPTION_STACK_OVERFLOW:
return true;
default:
return false;
}
}
LONG CALLBACK CrashDump::VectoredHandler(PEXCEPTION_POINTERS ep)
{
if (!ep || !ep->ExceptionRecord)
return EXCEPTION_CONTINUE_SEARCH;
DWORD code = ep->ExceptionRecord->ExceptionCode;
DWORD flags = ep->ExceptionRecord->ExceptionFlags;
// ⭐ 只处理真正致命异常
if (!IsFatalException(code, flags))
return EXCEPTION_CONTINUE_SEARCH;
if (InterlockedCompareExchange(&s_dumped, 1, 0) == 0)
{
WriteDump(ep);
// ❗真正崩溃点,立即终止
TerminateProcess(GetCurrentProcess(), code);
}
return EXCEPTION_CONTINUE_SEARCH;
}
void CrashDump::WriteDump(PEXCEPTION_POINTERS ep)
{
wchar_t dir[MAX_PATH] = { 0 };
GetModuleFileNameW(nullptr, dir, MAX_PATH);
wchar_t* slash = wcsrchr(dir, L'\\');
if (slash) *slash = 0;
SYSTEMTIME st;
GetLocalTime(&st);
DWORD pid = GetCurrentProcessId();
DWORD tid = GetCurrentThreadId();
wchar_t path[MAX_PATH] = { 0 };
StringCchPrintfW(
path,
MAX_PATH,
L"%s\\DumpFile-%04d%02d%02d-%02d%02d%02d-P%u-T%u.dmp",
dir,
st.wYear, st.wMonth, st.wDay,
st.wHour, st.wMinute, st.wSecond,
pid, tid);
HANDLE hFile = CreateFileW(
path,
GENERIC_WRITE,
0,
nullptr,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
nullptr);
if (hFile == INVALID_HANDLE_VALUE)
return;
MINIDUMP_EXCEPTION_INFORMATION mei = {};
mei.ThreadId = tid;
mei.ExceptionPointers = ep;
mei.ClientPointers = FALSE;
MINIDUMP_TYPE type = (MINIDUMP_TYPE)(
MiniDumpWithFullMemory |
MiniDumpWithThreadInfo |
MiniDumpWithHandleData |
MiniDumpWithUnloadedModules);
MiniDumpWriteDump(
GetCurrentProcess(),
pid,
hFile,
type,
&mei,
nullptr,
nullptr);
CloseHandle(hFile);
}
在main函数中调用:
cpp
CrashDump::Install();
QApplication a(argc, argv);
...
四、调试:
1、将dump文件放在对应的exe、pdb文件同级目录下;
2、
1)使用Visual Studio打开dump文件,然后设置符号路径,路径下需要有exe、pdb、dump;

2)点击"使用仅限本机进行调试",即可重新异常情况;