简体   繁体   English

备份捕获事件处理程序中抛出的异常

[英]Backup catching for exception thrown in EventHandler

I have a C# Program running as a Windows service doing some Network shenanigans I thought I had last-ditch "Log Fatal Errors" handling set up.我有一个 C# 程序作为 Windows 服务运行,执行一些网络恶作剧,我以为我已经设置了最后的“日志致命错误”处理设置。 But I've come across an edge case where the Service ends up dead but dodges those catches.但是我遇到了一个边缘情况,即服务最终死了但躲过了那些捕获。 :( :(

I believe this is caused by code throwing an Exception in the EventHandler registered to a .NET library's event.我相信这是由代码在注册到 .NET 库事件的 EventHandler 中抛出异常引起的。

Obviously I can (and should !) catch the Exception in my handler, but I'd like to understand how this is avoiding my fall-back error handling, and whether I can add some even more robust fall back logging, to ensure that I have some log records to analyse similar silent bugs in future.显然我可以(并且应该!)在我的处理程序中捕获异常,但我想了解这是如何避免我的回退错误处理,以及我是否可以添加一些更强大的回退日志记录,以确保我有一些日志记录,以便将来分析类似的无声错误。


The punchline of relevant code isn't terribly complex: 相关代码的重点并不是非常复杂:

ServiceBase.Run(container.Resolve<MyProjectWindowsService>()); in a try ...catch in Program.Main() MyProjectWindowsService : ServiceBase is the service object with an OnStop() implmentation.try ...catch in Program.Main() MyProjectWindowsService : ServiceBase是具有OnStop()的服务对象。 NetworkChange.NetworkAvailabilityChanged += CodeThatThrows;

But when that Exception is thrown, neither OnStop() nor the try...catch trigger.但是当抛出该异常时, OnStop()try...catch触发器都不会触发。 I can get it in a debugger, and it doesn't seem to go anywhere .. it just ... stops.我可以在调试器中获取它,但它似乎无处可......它只是......停止。

Fuller program details below, if you want them.如果您需要,请在下方查看更完整的计划详细信息。

How can I catch and log unhandled exceptions in Event Handlers registered to external library events?如何在注册到外部库事件的事件处理程序中捕获和记录未处理的异常?

(Also ... Is the behaviour I've described above the expected behaviour, or is there something slightly weird happening?) (另外......我上面描述的行为是预期的行为,还是发生了一些奇怪的事情?)


Program EXE entry point: 程序EXE入口点:

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();
        }
    }
}

Service Object服务对象

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 Registered to and ultimately invoked: 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(); } } }

I unfortunately can only speculate why the exception isn't being caught by your code (and I've kept that speculation to the comments)不幸的是,我只能推测为什么您的代码没有捕获异常(并且我已将这种推测保留在评论中)

However 2 events that might help you are,但是,有 2 个可能对您有帮助的事件是,

AppDomain.UnhandledException - this allows you to register a global handler for any unhandled exceptions in your application. AppDomain.UnhandledException - 这允许您为应用程序中的任何未处理的异常注册一个全局处理程序。 Here is the documentation https://docs.microsoft.com/en-us/dotnet/api/system.appdomain.unhandledexception?view=netframework-4.8这是文档https://docs.microsoft.com/en-us/dotnet/api/system.appdomain.unhandledexception?view=netframework-4.8

TaskScheduler.UnobservedTaskException - I've included this as I'm not familiar with the internals of the framework libraries you are using, but there maybe some asynchronous code happening somewhere, that is potentially not observing the result of a task. TaskScheduler.UnobservedTaskException - 因为我不熟悉您正在使用的框架库的内部结构,所以我将其包含在内,但可能在某处发生了一些异步代码,这可能没有观察到任务的结果。 If a faulted task (ie an exception was thrown) is never awaited or never has the Result property accessed and then goes out of scope so it can be garbage collected;如果错误任务(即抛出异常)从未被等待或从未访问过 Result 属性,然后超出范围,因此它可以被垃圾收集; at some indeterminate point in the future, it will get collected and an UnobservedTaskException will get thrown.在未来的某个不确定点,它将被收集并抛出 UnobservedTaskException。 Subscribing to this event, will let you handle that scenario.订阅此事件,将让您处理该场景。 Documentation here文档在这里

https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskscheduler.unobservedtaskexception?view=netframework-4.8 https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskscheduler.unobservedtaskexception?view=netframework-4.8

A little bit dig out what catches what on a WPF application :稍微挖掘一下 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);
                                      }
                                  });   

The output will be :输出将是:

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 ...

So FirstChanceException will be catching everything (even the handled ones) and the rest won't be catching anything.所以FirstChanceException将捕获所有内容(即使是处理过的),其余的将不会捕获任何内容。 My suggestion is modifying your example like this:我的建议是像这样修改你的例子:

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM