[英]Backup catching for exception thrown in EventHandler
我有一个 C# 程序作为 Windows 服务运行,执行一些网络恶作剧,我以为我已经设置了最后的“日志致命错误”处理设置。 但是我遇到了一个边缘情况,即服务最终死了但躲过了那些捕获。 :(
我相信这是由代码在注册到 .NET 库事件的 EventHandler 中抛出异常引起的。
显然我可以(并且应该!)在我的处理程序中捕获异常,但我想了解这是如何避免我的回退错误处理,以及我是否可以添加一些更强大的回退日志记录,以确保我有一些日志记录,以便将来分析类似的无声错误。
ServiceBase.Run(container.Resolve<MyProjectWindowsService>());
在try ...catch
in Program.Main()
MyProjectWindowsService : ServiceBase
是具有OnStop()
的服务对象。 NetworkChange.NetworkAvailabilityChanged += CodeThatThrows;
但是当抛出该异常时, OnStop()
和try...catch
触发器都不会触发。 我可以在调试器中获取它,但它似乎无处可去......它只是......停止。
如果您需要,请在下方查看更完整的计划详细信息。
如何在注册到外部库事件的事件处理程序中捕获和记录未处理的异常?
(另外......我上面描述的行为是预期的行为,还是发生了一些奇怪的事情?)
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using NLog;
using Exception = System.Exception;
namespace DNSProxy.Service
{
public class NetworkService
{
public NetworkService()
{
}
public bool NetworkDetectionEnabled
{
set
{
if (value)
{
NetworkChange.NetworkAvailabilityChanged += OnNetworkAvailabilityChanged;
NetworkChange.NetworkAddressChanged += OnNetworkAddressChanged;
}
else
{
NetworkChange.NetworkAvailabilityChanged -= OnNetworkAvailabilityChanged;
NetworkChange.NetworkAddressChanged -= OnNetworkAddressChanged;
}
}
}
private void OnNetworkAddressChanged(object sender, EventArgs e)
{
CodeThatCanApparentlyThrow();
}
private void OnNetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e)
{
CodeThatCanApparentlyThrow();
}
}
}
服务对象
using System; using System.ServiceModel; using System.ServiceProcess; using Autofac; using Autofac.Integration.Wcf; using NLog; namespace MyProject.Service { public class MyProjectWindowsService : ServiceBase { private readonly ILogger _logger; public DNSProxyWindowsService(ILogger logger) { ServiceName = Constants.SERVICE_NAME; _logger = logger; } protected override void OnStart(string[] args) { _logger.Info("=============================="); _logger.Info("DNProxy WindowsService Started"); _logger.Info("=============================="); //Other Active setupsteps } protected override void OnStop() { try { //Other Active shutdown steps. } catch (Exception ex) { _logger.Error(ex, "Could not shut down service tidily"); } finally { _logger.Info("=========================="); _logger.Info("WindowsService Stopped (1)"); _logger.Info("=========================="); } } } }
EventListener 注册并最终调用:
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; using NLog; using Exception = System.Exception; namespace DNSProxy.Service { public class NetworkService { public NetworkService() { } public bool NetworkDetectionEnabled { set { if (value) { NetworkChange.NetworkAvailabilityChanged += OnNetworkAvailabilityChanged; NetworkChange.NetworkAddressChanged += OnNetworkAddressChanged; } else { NetworkChange.NetworkAvailabilityChanged -= OnNetworkAvailabilityChanged; NetworkChange.NetworkAddressChanged -= OnNetworkAddressChanged; } } } private void OnNetworkAddressChanged(object sender, EventArgs e) { CodeThatCanApparentlyThrow(); } private void OnNetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e) { CodeThatCanApparentlyThrow(); } } }
不幸的是,我只能推测为什么您的代码没有捕获异常(并且我已将这种推测保留在评论中)
但是,有 2 个可能对您有帮助的事件是,
AppDomain.UnhandledException - 这允许您为应用程序中的任何未处理的异常注册一个全局处理程序。 这是文档https://docs.microsoft.com/en-us/dotnet/api/system.appdomain.unhandledexception?view=netframework-4.8
TaskScheduler.UnobservedTaskException - 因为我不熟悉您正在使用的框架库的内部结构,所以我将其包含在内,但可能在某处发生了一些异步代码,这可能没有观察到任务的结果。 如果错误任务(即抛出异常)从未被等待或从未访问过 Result 属性,然后超出范围,因此它可以被垃圾收集; 在未来的某个不确定点,它将被收集并抛出 UnobservedTaskException。 订阅此事件,将让您处理该场景。 文档在这里
稍微挖掘一下 WPF 应用程序上的内容:
var domain = AppDomain.CurrentDomain;
domain.UnhandledException += (o, args) =>
{
Debug.WriteLine("Catched in UnhandledException");
Debug.WriteLine(args.ExceptionObject);
};
domain.FirstChanceException += (o, args) =>
{
Debug.WriteLine("Catched in FirstChanceException");
Debug.WriteLine(args.Exception);
};
TaskScheduler.UnobservedTaskException += (o, args) =>
{
Debug.WriteLine("Catched in UnobservedTaskException");
Debug.WriteLine(args.Exception);
};
Task.Factory.StartNew(async () =>
{
Debug.WriteLine("Workinf");
await Task.Delay(1000);
try
{
throw new Exception("oops");
}
catch (Exception exception)
{
throw new Exception("oopps catched", exception);
}
});
输出将是:
Exception thrown: 'System.Exception' in WpfApp1.exe
Catched in FirstChanceException
System.Exception: oops
at ....
Exception thrown: 'System.Exception' in WpfApp1.exe
Catched in FirstChanceException
System.Exception: oopps catched ---> System.Exception: oops
at ...
--- End of inner exception stack trace ---
at ...
所以FirstChanceException
将捕获所有内容(即使是处理过的),其余的将不会捕获任何内容。 我的建议是像这样修改你的例子:
public class NetworkService
{
private readonly SynchronizationContext currContext;
public NetworkService()
{
this.currContext = SynchronizationContext.Current;
}
private void OnNetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e)
{
try
{
CodeThatCanApparentlyThrow();
}
catch (Exception exception)
{
this.currContext.Post(s => throw exception, null); // this will propagate your exception into main thread
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.