** WinForm全局异常处理的“全栈实战”**
第一阶段:基础全局异常处理机制
1.1 UI线程异常捕获
核心事件:Application.ThreadException
// 在Program.cs中配置
static class Program
{
[STAThread]
static void Main()
{
// 1. 订阅UI线程异常事件
Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);
// 2. 启用非UI线程异常捕获
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
// 3. 订阅全局未处理异常事件(非UI线程)
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
// UI线程异常处理
private static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
{
HandleException(e.Exception, "UI线程异常");
}
// 全局未处理异常处理(非UI线程)
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
HandleException(e.ExceptionObject as Exception, "非UI线程异常");
}
}
注释解析:
SetUnhandledExceptionMode
:必须设置为CatchException
,否则AppDomain
事件无法捕获非UI线程异常。HandleException
:统一异常处理逻辑,避免重复代码。
1.2 统一异常处理逻辑
/// <summary>
/// 统一异常处理逻辑(日志记录+用户提示+崩溃报告)
/// </summary>
private static void HandleException(Exception ex, string exceptionType)
{
if (ex == null) return;
// 1. 记录日志到文件
string logPath = Path.Combine(Environment.CurrentDirectory, "ErrorLog.txt");
File.AppendAllText(logPath, $"[{DateTime.Now}] {exceptionType}:\n{ex.ToString()}\n\n");
// 2. 弹出用户提示(UI线程安全)
if (Application.MessageLoop) // 确保在UI线程调用
{
MessageBox.Show(
$"发生严重错误:{ex.Message}\n\n请重启程序或联系管理员",
"系统错误",
MessageBoxButtons.OK,
MessageBoxIcon.Error
);
}
// 3. 发送崩溃报告(可选)
SendCrashReport(ex);
// 4. 安全退出程序(避免残留)
Application.Exit();
}
/// <summary>
/// 发送崩溃报告(模拟实现)
/// </summary>
private static void SendCrashReport(Exception ex)
{
try
{
// 实际开发中可替换为HTTP请求发送日志
Console.WriteLine($"模拟发送崩溃报告:{ex.Message}");
}
catch
{
// 忽略二次异常
}
}
注释解析:
Application.MessageLoop
:确保在UI线程弹窗,避免跨线程异常。SendCrashReport
:可扩展为发送邮件或HTTP请求,便于远程收集日志。
第二阶段:高级异常处理技巧
2.1 异步任务异常捕获
场景:Task
/async/await
引发的未处理异常
// 在按钮点击事件中使用异步操作
private async void btnAsync_Click(object sender, EventArgs e)
{
try
{
await Task.Run(() =>
{
throw new Exception("模拟异步异常"); // 未捕获时触发全局异常处理
});
}
catch (Exception ex)
{
// 本地处理异常(推荐)
MessageBox.Show($"本地捕获异常:{ex.Message}");
}
}
关键点:
- 异步代码的
try-catch
必须包裹await
表达式,否则异常会逃逸到Task
。 - 若未捕获,
AppDomain.UnhandledException
会接收到该异常。
2.2 避免二次崩溃的“金钟罩”设计
问题:异常处理代码自身抛出异常?
/// <summary>
/// 完全稳定的异常处理对话框(防二次崩溃)
/// </summary>
private static void SafeShowError(string message)
{
try
{
// 尝试在UI线程弹窗
if (Application.MessageLoop)
{
MessageBox.Show(message, "系统错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else
{
// 非UI线程:写入控制台/日志
Console.WriteLine($"致命错误:{message}");
}
}
catch
{
// 终极兜底:直接写入文件
File.AppendAllText("FatalError.txt", message);
}
}
注释解析:
- 多层容错:UI线程→控制台→文件写入。
- 确保无论如何都能记录错误信息。
第三阶段:实战案例与性能优化
3.1 实战案例:模拟崩溃并捕获
// 在Form中触发异常
private void btnCrash_Click(object sender, EventArgs e)
{
throw new DivideByZeroException("模拟除零异常"); // 触发全局UI线程异常
}
// 在后台线程触发异常
private void btnBackgroundCrash_Click(object sender, EventArgs e)
{
Task.Run(() =>
{
throw new NullReferenceException("模拟空引用异常"); // 触发非UI线程全局异常
});
}
3.2 性能优化:异步日志记录
/// <summary>
/// 异步日志记录(避免阻塞主线程)
/// </summary>
private static void LogExceptionAsync(Exception ex, string exceptionType)
{
Task.Run(() =>
{
try
{
string logPath = Path.Combine(Environment.CurrentDirectory, "ErrorLog.txt");
File.AppendAllText(logPath, $"[{DateTime.Now}] {exceptionType}:\n{ex.ToString()}\n\n");
}
catch
{
// 忽略日志记录失败
}
});
}
第四阶段:异常分类与分级处理
4.1 根据异常类型差异化处理
/// <summary>
/// 根据异常类型执行不同策略
/// </summary>
private static void HandleExceptionWithPolicy(Exception ex)
{
if (ex is UnauthorizedAccessException)
{
// 登录权限异常:跳转登录界面
MessageBox.Show("权限不足,请重新登录");
// Application.Restart(); // 重启程序
}
else if (ex is TimeoutException)
{
// 网络超时:重试或提示用户
MessageBox.Show("请求超时,正在重试...");
}
else
{
// 其他异常:记录日志并退出
HandleException(ex, "未分类异常");
}
}
4.2 异常重试机制
/// <summary>
/// 网络请求重试策略
/// </summary>
public static async Task<TResult> ExecuteWithRetry<TResult>(Func<Task<TResult>> action, int maxRetries = 3)
{
for (int attempt = 1; attempt <= maxRetries; attempt++)
{
try
{
return await action();
}
catch (Exception ex) when (ex is TimeoutException || ex is HttpRequestException)
{
if (attempt == maxRetries)
{
throw; // 最终抛出让全局处理
}
await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, attempt))); // 指数退避
}
}
throw new Exception("重试失败");
}
第五阶段:调试与监控
5.1 异常堆栈分析工具
/// <summary>
/// 提取异常的完整堆栈信息
/// </summary>
private static string GetFullStackTrace(Exception ex)
{
StringBuilder sb = new StringBuilder();
while (ex != null)
{
sb.AppendLine($"异常类型:{ex.GetType().Name}");
sb.AppendLine($"消息:{ex.Message}");
sb.AppendLine($"堆栈:{ex.StackTrace}");
ex = ex.InnerException;
}
return sb.ToString();
}
5.2 远程监控集成
/// <summary>
/// 发送崩溃报告到服务器(示例)
/// </summary>
private static void SendCrashReportToServer(Exception ex)
{
try
{
string crashInfo = GetFullStackTrace(ex);
using (var client = new WebClient())
{
client.UploadString(
"https://api.yourserver.com/crash-report",
$"exception={Uri.EscapeDataString(crashInfo)}"
);
}
}
catch
{
// 忽略网络错误
}
}