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. 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.
Obviously I can (andshould!) 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.
NetworkChange.NetworkAvailabilityChanged += CodeThatThrows;
But when that Exception is thrown, neither OnStop() nor the try...catch trigger.
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:
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("==========================");
}
}
}
}
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:
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,
AppDomain.UnhandledException - this allows you to register a global handler for any unhandled exceptions in your application. Here is the documentation https://learn.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. 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; at some indeterminate point in the future, it will get collected and an UnobservedTaskException will get thrown. Subscribing to this event, will let you handle that scenario. Documentation here
https://learn.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 :
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. 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
}
}
}
Related
I have a Prism WPF application which failed to load one of its modules when I deployed it (due to a database problem). On my development machine, I can see the relevant exceptions being thrown (and apparently caught and handled by Prism) in the Output window of Visual Studio.
I was able to solve the immediate problem by doing this:
public MyModuleViewConstructor()
{
try
{
// some startup work here
// ...
InitializeComponent();
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString(), "A System Error Occurred.");
}
}
Which surfaces the error in production so that I can take action on it. But I would still like to obtain the messages from any exceptions that are thrown during normal operations, So, in the spirit of Prism's way of doing things, I did this:
public class Logger : ILoggerFacade
{
public void Log(string message, Category category, Priority priority)
{
using (StreamWriter s = File.AppendText("Log.txt"))
{
s.WriteLine(string.Format("{0}-{1}: {2}", DateTime.Now.ToString("MM/dd/yyyy HH:mm:ss.ffff"), priority.ToString(), message));
s.Close();
}
}
}
And registered it in the Bootstrapper:
class Bootstrapper : DryIocBootstrapper
{
private readonly Logger _logger = new Logger();
protected override ILoggerFacade CreateLogger()
{
return _logger;
}
}
This works splendidly for normal log entries such as debug logging. But it still does not log any exceptions thrown within my application.
How do I get Prism to log thrown exceptions in my application?
When navigating, all exceptions that occur during view and/or view model creation are caught by the region manager. Those are not logged by default (although this would be a cool feature).
You can, however, be notified about the exception and log it yourself or react otherwise.
To do that, navigate through one of the IRegionManager.RequestNavigate( ..., Action<NavigationResult> navigationCallback ) overloads. The navigationCallback will be passed a result object that contains any exception in the NavigationResult.Error property.
The Prism logging mechanism is used mostly to log messages related to Prism events.
To use it for your events you could create and extension method like this
public static class LoggerExtensions
{
public static void Warn(this ILoggerFacade loger, string message)
{
using (StreamWriter s = File.AppendText("Log.txt"))
{
s.WriteLine(string.Format(" {0}-{1}: {2}", DateTime.Now.ToString("MM/dd/yyyy HH:mm:ss.ffff"), Category.Warn, message));
s.Close();
}
}
}
And to use it inside your code, you could do the following
var logger = _container.Resolve<ILoggerFacade>(); //If you use IoC
LoggerExtensions.Warn(logger, "Some exception...");
But it still does not log any exceptions thrown within my application
I would suggest to add Dispatcher.UnhandledException inside App.xaml.cs, so where ever there is any exception who is not handled, it will finish there.
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
this.Dispatcher.UnhandledException += OnDispatcherUnhandledException;
}
void OnDispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
{
//Your code
}
}
Update #2
I have created a small example, where to button click will throw DivideByZeroException. The Prism logging mechanism isn't aware of this exception at all. The only solution would be to use extension method, extend Exception class or other libraries.
I just don't see any other solution.
public partial class ViewA : UserControl
{
ILoggerFacade logger;
public ViewA(ILoggerFacade _logger)
{
InitializeComponent();
logger = _logger;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
try
{
var val = 0;
var result = 1500 / val;
}
catch (Exception ex)
{
LoggerExtensions.Warn(logger, ex.Message);
}
}
}
I am running the following line to get a list of all services for a given computer:
ServiceController[] services = ServiceController.GetServices(compName);
If I run this on the main thread and pass in a computer name that exists but I don't have permissions to view the services for, such as:
ServiceController.GetServices("OtherComp");
InvalidOperationException:
Cannot open Service Control Manager on computer 'OtherComp'. This operation might require other privileges.
I fully expect this to happen. However my issue comes with running this on a background thread. Take this fully complete console program:
using System.ComponentModel;
using System.ServiceProcess;
namespace ServiceTesting
{
internal class Program
{
private static void Main(string[] args)
{
ServiceAccessor sa = new ServiceAccessor();
sa.Run();
}
}
public class ServiceAccessor
{
BackgroundWorker bw;
public ServiceAccessor()
{
bw = new BackgroundWorker();
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerCompleted += new
RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
}
public void Run()
{
bw.RunWorkerAsync();
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
//this line is where the bail happens
var services = ServiceController.GetServices("OtherComputer");
}
void bw_RunWorkerCompleted(object sender,RunWorkerCompletedEventArgs e)
{
// the code will never get to this method
if (e.Error != null)
{
//something
}
}
}
}
I would expect an exception to be thrown, but as soon as the code tries to execute this line, it bails out of the thread.
I can't wrap the line in a try\catch; it will won't catch it. This might be simmilar to ThreadAbort problems with asp.net (but that is just a guess).
The msdn page on the ServiceController Class says that the static function is thread safe, however, a commenter on the function's msdn page says that it is not.
Exception was catched by BackgroundWorker internally, you can view it via RunWorkerCompleted event:
private void backgroundWorker1_RunWorkerCompleted(
object sender, RunWorkerCompletedEventArgs e)
{
// First, handle the case where an exception was thrown.
if (e.Error != null)
{
// deal with error
}
}
http://msdn.microsoft.com/en-us/library/system.componentmodel.runworkercompletedeventargs.aspx
UPD: However, it works as expected with Thread class:
new Thread(() =>
{
try
{
var services = ServiceController.GetServices("OtherComputer");
}
catch
{
}
}).Start();
I'm trying to add a default handler to my application so I can recover from otherwise unhandled exceptions.
I've found three mechanisms provided by Android/MonoDroid that, as far as I can tell, should make this possible, but I cannot get any of them to work. Here's my code:
using System;
using Android.App;
using Android.Content;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;
namespace TestApp {
[Android.App.Activity(Label = "TestApp", MainLauncher = true, Icon = "#drawable/icon")]
public class MainActivity : Activity {
protected override void OnCreate(Bundle bundle) {
base.OnCreate(bundle);
SetContentView(new LinearLayout(this));
//set up handlers for uncaught exceptions:
//Java solution
Java.Lang.Thread.DefaultUncaughtExceptionHandler = new ExceptionHandler(this);
//MonoDroid solution #1
AndroidEnvironment.UnhandledExceptionRaiser += AndroidEnvironment_UnhandledExceptionRaiser;
//MonoDroid solution #2
AppDomain.CurrentDomain.UnhandledException += delegate { new Alert(this, "AppDomain.CurrentDomain.UnhandledException", "error"); };
//throw an exception to test
throw new Exception("uncaught exception");
}
void AndroidEnvironment_UnhandledExceptionRaiser(object sender, RaiseThrowableEventArgs e)
{
//found a suggestion to set the Handled flag=true, but it has no effect
new Android.Runtime.RaiseThrowableEventArgs(e.Exception).Handled = true;
new Alert(this, "AndroidEnvironment.UnhandledExceptionRaiser", "error");
}
}
public class ExceptionHandler : Java.Lang.Object, Java.Lang.Thread.IUncaughtExceptionHandler {
private Context _context;
public ExceptionHandler(Context c) { _context = c; }
public void UncaughtException(Java.Lang.Thread thread, Java.Lang.Throwable ex) {
new Alert(_context, "java exception handler");
}
}
public class Alert {
public Alert(Context c, string src, string title = "alert") {
Android.App.AlertDialog.Builder builder = new Android.App.AlertDialog.Builder(c);
builder.SetTitle(title);
builder.SetMessage(src);
builder.SetPositiveButton("Ok", delegate { });
Android.App.AlertDialog alert = builder.Create();
alert.Show();
}
}
}
Any help would be appreciated, thanks!
You can catch most of exceptions in AppDomain.CurrentDomain.UnhandledException event. But you can't call AlertDialog in this delegate, because:
1. Application is crashed.
2. UnhandledException event is called at least.
3. alert.Show() is executed async (in UI thread)
So, you should use sync operations (System.IO, for example). You shouldn't use native UI operations, because they are async.
I am using a singleton pattern for a global event manager class that is not handling exceptions in an acceptable manner.
If an exception is thrown in the code called by one of the events being executed, I always get a Exception has been thrown by the target of an invocation. error. This error contains no information related to the original exception, making it extremely difficult to debug any errors.
Is there some way to pass the original exception information back to the event manager?
public class ApplicationSettings
{
private static EventManager _manager = new EventManager();
public static EventManager EventManager
{
get { return _manager; }
}
}
The event manager class:
public class EventManager
{
public event EventHandler<ReportExecutionArgs> ExecuteReportCurrentPage;
public event EventHandler<ReportExecutionArgs> ExecuteReportNewPage;
public virtual void OnExecuteReportCurrentPage(object sender, ReportExecutionArgs e)
{
try
{
if (this.ExecuteReportCurrentPage != null)
this.ExecuteReportCurrentPage(sender, e);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
public virtual void OnExecuteReportNewPage(object sender, ReportExecutionArgs e)
{
try
{
if (this.ExecuteReportNewPage != null)
this.ExecuteReportNewPage(sender, e);
}
catch(Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
}
Some other class will handle these events
ApplicationSettings.EventManager.ExecuteReportNewPage += new EventHandler<ReportExecutionArgs>(reportOpenedNewPage);
private void reportOpenedNewPage(object sender, ReportExecutionArgs e)
{
//something in this code throws an error
LitePage page = new LitePage();
_tabs.AddPage(page);
Report report = setReport(page, e);
}
EDIT
Just to clarify, the try/catch blocks in the OnExecuteReport methods are not catching the exception.
A TargetInvocationException such as the one you describe will almost always have the originating exception in it's InnerException.
I am trying to log my exceptions with AppDomain.CurrentDomain.UnhandledException.
AppDomain.CurrentDomain.UnhandledException += AppDomainUnhandledException;
public static void AppDomainUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
HandleException(e.ExceptionObject as Exception);
}
When I get an exception in the server I will get the fault in the client at debug time but the event hooked to AppDomain.CurrentDomain.UnhandledException will never be fired.
Server:
[ServiceBehavior(IncludeExceptionDetailInFaults=true)]
public class Service : IService
{
public string GetError()
{
throw new ApplicationException();
}
}
Client:
public void GetError()
{
this.service.BeginGetError(OnGetErrorCompleted, null);
}
public void OnGetErrorCompleted(IAsyncResult result)
{
var value = this.service.EndGetError(result);
}
This doesn't work UNLESS I use Distpacher.BeginInvoke..
public void OnGetErrorCompleted(IAsyncResult result)
{
this.Dispatcher.BeginInvoke((Action)(() =>
{
var value = this.service.EndGetError(result);
}));
}
Somebody knows why??
I thought AppDomain.CurrentDomain.UnhandledException would be fired in case of an exception in ANY thread! :S
A better solution is to use something like IErrorHandler and throw FaultExceptions. Decorate your operations to throw FaultExceptions using the FaultContract attribute. Now you can catch specific FaultExceptions at the client. You should not expose exceptions on the server to the client.