[英]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.