ProcDump 抓取程序闪退 Dump 及 WinDbg 分析流程
ProcDump 抓取程序闪退 Dump 及 WinDbg 分析流程
1. 背景说明
R程序是 C# WinForms 工业控制软件。正常情况下,托管异常可以通过以下入口记录日志:
Application.ThreadException
AppDomain.CurrentDomain.UnhandledException
try/catch
但这些机制不能覆盖所有崩溃。以下情况可能导致程序直接退出,C# 异常处理代码来不及执行:
- 非托管 DLL 崩溃。
- CLR FastFail。
- 栈破坏或安全检查失败。
- 图形库、驱动、GDI/Skia 等底层组件崩溃。
- 进程级异常,例如
0xc0000409。
这类问题通常表现为:
- 程序无异常弹窗直接闪退。
- 项目日志没有有效异常。
- Windows 事件日志只显示
clr.dll、ntdll.dll、KERNELBASE.dll等模块。 - 公司设备无法复现,客户现场能复现。
这种情况不能只靠程序日志定位,必须抓取进程崩溃 Dump。
2. 适用场景
建议使用 CrashDumpHelper、ProcDump、WinDbg 的场景:
- RTP.exe 运行中突然退出。
Application_ThreadException没有记录异常。CurrentDomain_UnhandledException没有记录异常。- Windows 事件查看器中有应用程序错误记录。
- 异常代码类似:
0xc0000409
0xc0000005
0xe0434352
- 客户现场偶发,公司设备无法复现。
3. 使用软件和机制
3.1 CrashDumpHelper
CrashDumpHelper 是程序内置的 WER LocalDumps 自动配置代码。程序启动时自动创建 Dump 目录并写入注册表。
优点:
- 不需要现场工程师手动注册 ProcDump。
- 程序每次启动时自动检查配置。
- Dump 默认输出到程序目录下的
CrashDumps。
限制:
- 依赖 Windows Error Reporting。
- 依赖程序目录写入权限。
- 某些系统策略或权限限制下可能无法生成 Dump。
3.2 ProcDump
ProcDump 是 Sysinternals 工具,用于在进程崩溃时自动生成 Dump 文件。
下载地址:
https://learn.microsoft.com/sysinternals/downloads/procdump
优点:
- 现场抓崩溃证据能力强。
- 适合客户现场复现、公司无法复现的问题。
- 可作为 JIT 调试器捕获崩溃。
3.3 WinDbg Preview
WinDbg 用于分析 .dmp 文件。
推荐从 Microsoft Store 安装 WinDbg Preview。
4. 程序内置 CrashDumpHelper 自动配置 WER Dump
除了外部使用 ProcDump,本项目还可以在程序启动时自动配置 Windows Error Reporting LocalDumps。这样即使现场没有提前注册 ProcDump,程序发生进程级崩溃时,也有机会在程序目录下自动生成 Dump。
4.1 新增 CrashDumpHelper.cs
文件位置:
RTP\2_BLL\CrashDumpHelper.cs
核心作用:
- 创建
CrashDumps目录。 - 写入 WER LocalDumps 注册表。
- 设置 Dump 类型为完整 Dump。
- 同时尝试写入 CurrentUser、LocalMachine、32 位、64 位注册表视图。
- 返回配置结果并写入系统日志。
核心注册表路径:
Software\Microsoft\Windows\Windows Error Reporting\LocalDumps\RTP.exe
关键注册表项:
DumpFolder = 程序目录\CrashDumps
DumpCount = 10
DumpType = 2
说明:
DumpType = 1:Mini Dump。DumpType = 2:Full Dump,推荐用于现场排查。DumpCount = 10:最多保留 10 个 Dump。
示例代码结构:
public static class CrashDumpHelper
{
private const string LocalDumpsRegistryPath =
@"Software\Microsoft\Windows\Windows Error Reporting\LocalDumps";
public static string DumpFolder { get; private set; }
public static bool EnsureWerLocalDump(out string message)
{
string exeName = Path.GetFileName(Application.ExecutablePath);
string dumpFolder = Path.Combine(Application.StartupPath, "CrashDumps");
Directory.CreateDirectory(dumpFolder);
DumpFolder = dumpFolder;
// 写入 HKCU/HKLM + 32/64 位注册表视图。
// 写入 DumpFolder、DumpCount、DumpType。
}
}
4.2 在 Program.cs 启动时调用
调用位置:
RTP\Program.cs
建议在程序确认目录可写后尽早调用:
bool dumpConfigured = CrashDumpHelper.EnsureWerLocalDump(out string dumpMessage);
Global.Init();
Global.GSysLog.WriteMsg(dumpMessage, dumpConfigured ? LogType.Sys : LogType.Err);
推荐启动顺序:
1. 注册全局异常处理。
2. 检查程序目录写入权限。
3. 调用 CrashDumpHelper.EnsureWerLocalDump()。
4. Global.Init()。
5. 写入系统日志。
6. 启动登录窗体或主窗体。
4.3 为什么要先检查程序目录写入权限
Dump 默认写到:
Application.StartupPath\CrashDumps
如果程序安装在 C:\Program Files 或 C 盘受控目录,普通用户可能没有写入权限。此时即使 WER 配置成功,也可能无法生成 Dump。
因此程序启动时需要先检查:
Global.CanWriteFile(Application.StartupPath, out error)
如果没有写入权限,应提示:
当前用户没有程序目录写入权限,程序需要管理员权限运行。
请右键程序图标,选择“以管理员身份运行”。
4.4 CrashDumpHelper 和 ProcDump 的关系
两者不是互斥关系。
推荐策略:
程序内置 CrashDumpHelper:默认启用,作为基础兜底。
外部 ProcDump:现场问题严重、WER 没生成 Dump、需要更强捕获能力时使用。
优先级建议:
- 软件内置
CrashDumpHelper,每次启动自动配置。 - 如果客户现场仍然没有 Dump,再使用 ProcDump 注册 JIT。
- 如果问题极难复现,可以长期保留 ProcDump 注册,复现后再取消。
4.5 内置 WER Dump 没生成时的排查
如果 CrashDumps 目录没有文件,检查:
- 程序目录是否有写入权限。
CrashDumpHelper.EnsureWerLocalDump()是否执行。- 系统日志中是否记录 WER 配置失败。
- 注册表是否存在:
HKCU\Software\Microsoft\Windows\Windows Error Reporting\LocalDumps\RTP.exe
HKLM\Software\Microsoft\Windows\Windows Error Reporting\LocalDumps\RTP.exe
- 程序是否是被任务管理器结束,而不是崩溃。
- Windows Error Reporting 服务是否被系统策略禁用。
- 杀毒软件是否拦截 Dump 写入。
5. ProcDump 外部抓 Dump
5.1 创建 Dump 目录
建议放在程序目录下,例如:
New-Item -ItemType Directory -Force "D:\RTP\bin\Debug\CrashDumps"
客户现场也可以放到程序同级目录:
RTP\bin\Debug\CrashDumps
5.2 注册 ProcDump 为 JIT 调试器
以管理员权限打开 PowerShell,进入 ProcDump 所在目录:
cd C:\Users\cjt\Downloads\Procdump
执行:
.\procdump.exe -ma -i D:\RTP\bin\Debug\CrashDumps
参数说明:
-ma:生成完整用户态 Dump,包含更多内存信息。-i:注册为系统 Just-In-Time 调试器,程序崩溃时自动抓 Dump。- 后面的路径:Dump 输出目录。
注册成功后会看到类似输出:
ProcDump is now set as the Just-in-time (AeDebug) debugger.
注册后,客户正常运行 RTP 程序即可。程序闪退时,ProcDump 会自动生成 .dmp 文件。
5.3 Dump 文件命名
生成文件通常类似:
RTP.exe_260710_163823.dmp
含义:
RTP.exe:崩溃进程名。260710:日期。163823:时间。
5.4 取消 ProcDump 注册
问题排查结束后,可以取消注册:
.\procdump.exe -u
6. 没有生成 Dump 的排查
如果程序闪退后没有 .dmp 文件,检查以下事项:
- 是否以管理员权限执行了
procdump -i。 - Dump 目录是否存在。
- Dump 目录是否有写入权限。
- 注册的是 32 位和 64 位 AeDebug 还是只注册了一边。
- 程序是否真的崩溃退出,而不是被任务管理器结束。
- 是否有杀毒软件拦截 ProcDump。
- 是否路径中包含不可访问的网络目录。
可以重新执行注册命令确认:
.\procdump.exe -ma -i D:\RTP\bin\Debug\CrashDumps
7. WinDbg 分析 Dump
7.1 打开 Dump
打开 WinDbg Preview,选择:
File -> Open dump file
选择生成的 .dmp 文件。
7.2 输入命令的位置
WinDbg 底部有命令输入框,提示符通常类似:
0:026>
在这里输入命令。
7.3 设置符号
打开 Dump 后先执行:
.symfix
.reload
如果需要指定缓存目录,可以使用:
.sympath srv*C:\Symbols*https://msdl.microsoft.com/download/symbols
.reload
7.4 基础分析命令
常用命令:
!analyze -v
.ecxr
kb
k
~* kb
lm
含义:
!analyze -v:自动分析异常原因。.ecxr:切换到异常上下文。kb:查看当前线程调用栈。~* kb:查看所有线程调用栈。lm:查看加载模块。
7.5 .NET 程序相关命令
如果需要查看托管栈:
.loadby sos clr
!clrstack
!threads
!pe
如果是 .NET 4.x 程序,通常使用:
.loadby sos clr
如果 !clrstack 无法显示有效信息,说明当前异常可能不是普通托管异常,或者当前线程不在托管执行上下文中。
8. 本项目案例复盘
8.1 Windows 事件日志现象
客户反馈退火完成后程序闪退,公司设备无法稳定复现。
Windows 事件日志中出现类似:
错误应用程序名称: RTP.exe
错误模块名称: clr.dll
异常代码: 0xc0000409
错误模块路径: C:\Windows\Microsoft.NET\Framework\v4.0.30319\clr.dll
这类日志只能说明进程崩溃,不能直接定位到业务代码。
8.2 ProcDump 抓到 Dump
使用命令:
.\procdump.exe -ma -i D:\RTP\bin\Debug\CrashDumps
崩溃后得到:
RTP.exe_260710_163823.dmp
8.3 WinDbg 关键输出
WinDbg 打开 Dump 后显示:
Security check failure or stack buffer overrun - code c0000409
Subcode: 0x2 FAST_FAIL_STACK_COOKIE_CHECK_FAILURE
clr!__report_gsfailure
关键信息:
0xc0000409:安全检查失败或栈缓冲区破坏。FAST_FAIL_STACK_COOKIE_CHECK_FAILURE:栈 cookie 检查失败。clr!__report_gsfailure:CLR 报告安全失败。
这不是普通 C# Exception,所以 try/catch、Application.ThreadException、UnhandledException 可能都捕获不到。
8.4 调用栈判断
结合线程栈分析,崩溃路径指向曲线控件渲染相关调用,涉及 ScottPlot / SkiaSharp。
判断:
ScottPlot / SkiaSharp 渲染路径存在非托管崩溃风险。
因此最终处理方向不是继续加 try/catch,而是降低非托管图形库风险,将曲线控件迁移到 WinForms/GDI+ 自绘方案。
9. 如何判断崩溃类型
9.1 普通托管异常
特征:
- 日志中有 C# 异常类型。
- 有业务代码堆栈。
Application.ThreadException或UnhandledException可能记录到。
处理:
- 修业务逻辑。
- 增加边界判断。
- 增加必要日志。
9.2 未处理托管异常
特征:
- 异常代码可能是
0xe0434352。 - WinDbg 中
!pe可能能看到托管异常对象。
处理:
- 看
!clrstack。 - 修异常源头。
9.3 非托管崩溃
特征:
0xc0000005访问冲突。0xc0000409安全失败。- 栈里有 native dll、图形库、驱动、第三方组件。
- C# 日志可能没有任何记录。
处理:
- 用 Dump 定位 native 调用路径。
- 升级或替换第三方库。
- 避免在高频 UI 刷新中使用高风险 native 路径。
9.4 CLR FastFail
特征:
FAST_FAIL_*
clr!__report_gsfailure
处理:
- 不能依赖 C# 捕获。
- 必须从 Dump 调用栈分析。
- 优先排查 native 库、unsafe 代码、P/Invoke、图形渲染、驱动交互。
10. 客户现场标准流程
客户现场遇到闪退,建议按以下流程执行:
- 保留程序目录,不要覆盖现场文件。
- 导出 Windows 事件日志。
- 确认程序启动日志中是否有 CrashDumpHelper 配置结果。
- 检查程序目录下是否存在
CrashDumps。 - 如果内置 WER Dump 未生成,管理员权限注册 ProcDump。
- 让客户按正常流程操作并复现问题。
- 收集
.dmp文件。 - 收集程序日志目录。
- 收集配置文件。
- 使用 WinDbg 分析 Dump。
- 输出结论:托管异常、非托管崩溃、第三方库、驱动或业务代码。
建议客户现场保留文件:
RTP.exe_*.dmp
Log\
App.xml
Windows 事件日志截图或导出文件
复现操作步骤
复现时间点
11. 常用命令速查
11.1 ProcDump
注册为崩溃 Dump 捕获器:
.\procdump.exe -ma -i D:\RTP\bin\Debug\CrashDumps
取消注册:
.\procdump.exe -u
查看帮助:
.\procdump.exe -?
11.2 WinDbg
基础命令:
.symfix
.reload
!analyze -v
.ecxr
kb
~* kb
lm
.NET 命令:
.loadby sos clr
!clrstack
!threads
!pe
11.3 Windows 事件查看器
路径:
事件查看器 -> Windows 日志 -> 应用程序
重点看:
错误应用程序名称
错误模块名称
异常代码
错误偏移量
错误应用程序路径
错误模块路径
12. 经验结论
- C# 异常处理不是万能的。
- 进程级崩溃必须抓 Dump。
CrashDumpHelper是程序内置兜底方案。- ProcDump 是客户现场外部增强抓取方案。
- 客户现场问题优先保留证据,不要先覆盖程序。
clr.dll出现在事件日志里,不代表一定是 C# 业务异常。0xc0000409、FAST_FAIL_*要优先怀疑 native 组件、图形库、驱动或内存破坏。- 工业控制软件中,高频 UI 渲染应尽量避免高风险 native 图形链路。
- Dump 分析结论要结合程序日志、现场操作步骤、Windows 事件日志一起判断。
浙公网安备 33010602011771号