之前的方案容易导致软件正常运行时崩溃:
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、最小副作用,可长期运行