备份捕获EventHandler中引发的异常

时间:2019-12-10 11:21:29

标签: c# exception event-handling windows-services

我有一个C#程序作为Windows服务运行,做了一些网络设计 我以为我已设置了最后一条“记录致命错误”处理程序。但是我遇到了一个极端情况,即该服务最终死了,但躲避了这些陷阱。 :(

我相信这是由于代码在注册到.NET库事件的EventHandler中引发异常所致。

很明显,我可以(并且应该!)在我的处理程序中捕获该异常,但是我想了解这是如何避免我的后备错误处理的,以及是否可以添加甚至更强大的后备日志记录,以确保将来有一些日志记录来分析类似的静默错误。


相关代码的重点并不十分复杂:

ServiceBase.Run(container.Resolve<MyProjectWindowsService>());try ...catch中的

Program.Main() MyProjectWindowsService : ServiceBase是带有OnStop()实现的服务对象。 NetworkChange.NetworkAvailabilityChanged += CodeThatThrows;

但是,当引发该异常时,OnStop()try...catch都不会触发。 我可以在调试器中获取它,而且似乎没有 ..它只是...停止了。

如果需要,可以在下面查看更完整的程序详细信息。

如何在注册到外部库事件的事件处理程序中捕获和记录未处理的异常?

(还……我上面描述的行为是预期行为,还是发生了一些奇怪的事情?)


程序EXE入口点:

using System;
using System.ServiceProcess;
using Autofac;
using Autofac.Extras.NLog;
using NLog;

namespace MyProject.Service
{
    internal static class Program
    {
        private static readonly ILogger Logger = LogManager.GetCurrentClassLogger();

        /// <summary>
        ///     The main entry point for the application.
        /// </summary>
        private static void Main()
        {
            try
            {
                // var container = ...DI Setup...
                ServiceBase.Run(container.Resolve<MyProjectWindowsService>());
            }
            catch (Exception ex)
            {
                Logger.Error(ex, "Unexpected error");
            }
            finally
            {
                Logger.Info("==========================");
                Logger.Info("WindowsService Stopped (2)");
                Logger.Info("==========================");
            }

        }
    }
}

服务对象

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 个答案:

答案 0 :(得分:1)

不幸的是,我只能推测为什么您的代码未捕获到异常(并且我一直将这种推测保留在注释中)

不过有2件事可能对您有帮助

AppDomain.UnhandledException -这允许您为应用程序中任何未处理的异常注册全局处理程序。这是文档https://docs.microsoft.com/en-us/dotnet/api/system.appdomain.unhandledexception?view=netframework-4.8

TaskScheduler.UnobservedTaskException -由于我不熟悉所使用的框架库的内部结构,因此已将其包含在内,但是可能在某处发生了一些异步代码,这可能无法观察任务的结果。如果从未等待过错误的任务(即引发了异常),或者从未访问过Result属性,然后超出了范围,则可以对其进行垃圾回收;在将来的某个不确定时刻,它将被收集,并且将抛出UnobservedTaskException。订阅此事件,将使您处理该情况。这里的文档

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

答案 1 :(得分:0)

稍微了解一下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
            }

        }
    }