I'm using the following code to display unhandled exceptions in a WPF application:
public MyApplication() {
this.DispatcherUnhandledException += (o, e) => {
var exceptionMessage = new ExceptionWindow();
exceptionMessage.ExceptionMessage.Text = e.Exception.Message;
exceptionMessage.ExceptionCallStack.Text = e.Exception.StackTrace;
exceptionMessage.ExceptionInnerException.Text = e.Exception.InnerException.Message;
exceptionMessage.WindowStartupLocation = WindowStartupLocation.CenterScreen;
exceptionMessage.WindowStyle = WindowStyle.ToolWindow;
exceptionMessage.ShowDialog();
e.Handled = true;
Shell.Close();
};
}
Turns out that I have an exception during the instantiation of the application, so the app constructor is never executed.
A simple way to reproduce it (with a different exception) is by introducing an extra "<" before some tag in your app's configuration file and run it.
A useless error message like that appears before the application constructor get called.
alt text http://srtsolutions.com/cfs-filesystemfile.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/mikewoelmer/ExceptionWPF1_5F00_1C1F39AA.jpg
Does anyone know how to catch such kind of exceptions?
Remark: I'm using Caliburn and my application extends CaliburnApplication.
Okay. I solved the problem by doing the following:
Change the Build Action of the App.xaml file from ApplicationDefinition to Page.
Create a new class like following:
public class AppStartup {
[STAThread]
static public void Main(string[] args) {
try {
App app = new App();
app.InitializeComponent();
app.Run();
}
catch (Exception e) {
MessageBox.Show(e.Message + "\r\r" + e.StackTrace, "Application Exception", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
It replaces the generated App.g.cs Main method by this one, so we have a chance to catch the exceptions.
Related
We're having a winforms application that uses an async initialization process. Simplified you can say that the application will run the following steps:
Init - this runs async
Show MainForm
Application.Run()
The currently existing and working code looks like this:
[STAThread]
private static void Main()
{
SynchronizationContext.SetSynchronizationContext(new WindowsFormsSynchronizationContext());
var task = StartUp();
HandleException(task);
Application.Run();
}
private static async Task StartUp()
{
await InitAsync();
var frm = new Form();
frm.Closed += (_, __) => Application.ExitThread();
frm.Show();
}
private static async Task InitAsync()
{
// the real content doesn't matter
await Task.Delay(1000);
}
private static async void HandleException(Task task)
{
try
{
await Task.Yield();
await task;
}
catch (Exception e)
{
Console.WriteLine(e);
Application.ExitThread();
}
}
The background how this is working is described very detailed by Mark Sowul here.
Since C# 7.1 we're able to use async Task in main method. We tried it in a straight forward way:
[STAThread]
private static async Task Main()
{
SynchronizationContext.SetSynchronizationContext(new WindowsFormsSynchronizationContext());
try
{
await StartUp();
Application.Run();
}
catch (Exception e)
{
Console.WriteLine(e);
Application.ExitThread();
}
}
private static async Task StartUp()
{
await InitAsync();
var frm = new Form();
frm.Closed += (_, __) => Application.ExitThread();
frm.Show();
}
private static async Task InitAsync()
{
// the real content doesn't matter
await Task.Delay(1000);
}
But that doesn't work. The reason is clear. All the code after the first await will be forwarded to the message loop. But the message loop hasn't startet yet because the code that starts it (Application.Run()) is located after the first await.
Removing the synchronization context will fix the problem but causes to run the code after await in a different thread.
Reordering the code to call Application.Run() before the first await will not work because it is a blocking call.
We try to use the new feature of having an async Task Main() that allows us to remove the HandleException-solution that is hard to understand. But we don't know how.
Do you have any suggestions?
You don't need async Main. Here is how it can possibly be done:
[STAThread]
static void Main()
{
void threadExceptionHandler(object s, System.Threading.ThreadExceptionEventArgs e)
{
Console.WriteLine(e);
Application.ExitThread();
}
async void startupHandler(object s, EventArgs e)
{
// WindowsFormsSynchronizationContext is already set here
Application.Idle -= startupHandler;
try
{
await StartUp();
}
catch (Exception)
{
// handle if desired, otherwise threadExceptionHandler will handle it
throw;
}
};
Application.ThreadException += threadExceptionHandler;
Application.Idle += startupHandler;
try
{
Application.Run();
}
catch (Exception e)
{
Console.WriteLine(e);
}
finally
{
Application.Idle -= startupHandler;
Application.ThreadException -= threadExceptionHandler;
}
}
Note, if you don't register threadExceptionHandler event handler and StartUp throws (or anything else on the message loop throws, for the matter), it will still work. The exception will be caught inside the try/catch which wraps Application.Run. It will just be a TargetInvocationException exception with the original exception available via its InnerException property.
Updated to address the comments:
But for me it looks very strange to register an EventHandler to the
idle event so startup the whole application. It's totally clear how
that works but still strange. In that case I prefer the
HandleException solution that I already have.
I guess it's a matter of taste. I don't know why WinForms API designers didn't provide something like WPF's Application.Startup. However, in the lack of a dedicated event for this on WinForm's Application class, deferring specific initialization code upon the first Idle event is IMO an elegant solution, and it's widely used here on SO.
I particularly don't like the explicit manual provisioning of WindowsFormsSynchronizationContext before Application.Run has started, but if you want an alternative solution, here you go:
[STAThread]
static void Main()
{
async void startupHandler(object s)
{
try
{
await StartUp();
}
catch (Exception ex)
{
// handle here if desired,
// otherwise it be asynchronously propogated to
// the try/catch wrapping Application.Run
throw;
}
};
// don't dispatch exceptions to Application.ThreadException
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException);
using (var ctx = new WindowsFormsSynchronizationContext())
{
System.Threading.SynchronizationContext.SetSynchronizationContext(ctx);
try
{
ctx.Post(startupHandler, null);
Application.Run();
}
catch (Exception e)
{
Console.WriteLine(e);
}
finally
{
System.Threading.SynchronizationContext.SetSynchronizationContext(null);
}
}
}
IMO, either approach is more clean than the one used in your question. On a side note, you should be using ApplicationContext to handle the form closure. You can pass an instance of ApplicationContext to Application.Run.
The only point that I'm
missing is your hint that the synchronization context is already set.
Yes it is - but why?
It is indeed set as a part of Application.Run, if not already present on the current thread. If you like to learn more details, you could investigate it in .NET Reference Source.
Hi currently I am trying to check if the Ria Service is available for our OOB application.
public static void IsServiceReachable(Action onServiceAvailable, Action onServiceUnavailable)
{
try {
DomainContext context = new DomainContext();
InvokeOperation<bool> invokeOperation = context.IsAlive();
invokeOperation.Completed += (s, arg) => onServiceAvailable();
}
catch (Exception) {
onServiceUnavailable();
}
}
When the exception happen my App hangs, and is now just a white screen. Am I doing it correctly?
I am also using MEF in the app, I am lazy importing my views, sadly when Ria Service is not reachable, MEF doesnt import my views :( I am calling CompositionInitializer.SatisfyImports(this).
[ImportMany(AllowRecomposition = true)]
public Lazy<BaseUserControl, IViewMetadata>[] Views { get; set; }
Have you tried checking if an error has occured in the OnServiceAvailable callback:
void OnServiceAvailable(object sender, EventArgs e)
{
InvokeOperation op = sender as InvokeOperation;
if (op.HasError) {
Exception exception = op.Error;
...
} else {
...
}
}
You should probably rename OnServiceAvailable something like OnOperationComplete.
You have to handle the errors in the callback - including the 'ServiceNotAvailable' error. Remember this is an asyncronous call - the client does does not wait for the server response before it continues.
so I was trying to add "AppDomain.CurrentDomain.UnhandledException" handler to my application and it worked OK if I log the error to a text file. but when I try to use a MessageBox it never pops. is it another bug in .Net? any ideas?
here is my code sample:
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
and here is my handler method:
static void CurrentDomain_UnhandledException (object sender, UnhandledExceptionEventArgs e)
{
try
{
Exception ex = (Exception)e.ExceptionObject;
MessageBox.Show("Whoops! Please contact the developers with "
+ "the following information:\r\n\r\n" + ex.Message + ex.StackTrace,
"Fatal Error", MessageBoxButtons.OK, MessageBoxIcon.Stop);
}
finally
{
Application.Exit();
}
}
EDIT: I've tried all of the possible options but I still can't see the MessageBox. Now the problem is when I run it from Visual C# (debug mode) it perfectly shows the box. but when I run the application directly from the debug/release folders it doesn't shows the MessageBox and the Application keeps running like there is no error is happening...
this example works for me in debug mode and release mode with vs2010 or not:
using System;
using System.Windows.Forms;
namespace WinFormsStackOverflowSpielWiese
{
internal static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
private static void Main() {
System.Windows.Forms.Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
System.Windows.Forms.Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
private static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e) {
try {
var exception = e.Exception != null ? e.Exception.Message + e.Exception.StackTrace : string.Empty;
MessageBox.Show("Whoops! Please contact the developers with the following information:\r\n\r\n" + exception,
"Fatal Error", MessageBoxButtons.OK, MessageBoxIcon.Stop);
}
finally {
Application.Exit();
}
}
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) {
try {
var exception = e.ExceptionObject is Exception ? ((Exception)e.ExceptionObject).Message + ((Exception)e.ExceptionObject).StackTrace : string.Empty;
MessageBox.Show("Whoops! Please contact the developers with the following information:\r\n\r\n" + exception,
"Fatal Error", MessageBoxButtons.OK, MessageBoxIcon.Stop);
}
finally {
Application.Exit();
}
}
}
}
code for the form
EDIT now with a timer, with the same result....
using System.Windows.Forms;
namespace WinFormsStackOverflowSpielWiese
{
public partial class Form1 : Form
{
private System.Threading.Timer myTimer;
public Form1() {
this.InitializeComponent();
this.myTimer = new System.Threading.Timer(state =>
{
var i = 0;
var s = 100 / i;
}
, null, 5000, 5000);
}
}
}
The case might be because Unhandled Exceptions cause the application to terminate silently and UnhandledExceptionEventHandler handles non-UI thread exceptions.
See Application.SetUnhandledExceptionMode Method, AppDomain.UnhandledException Event and Application.ThreadException Event
EDIT:
Try setting Application.SetUnhandledExceptionMode as per first link:
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException); //add this line
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
Hi I try log expcetion on App Domain with NLog. It is WPF app with Caliburn Micro.
In MEF bootstraper I have this code:
static readonly ILog Log = LogManager.GetLog(typeof(NLogLogger));
#region Constructors
public MefBootStrapper()
: base()
{
_msgBox = new MessageBoxes();
_doHandle = true;
}
static MefBootStrapper()
{
LogManager.GetLog = type => new NLogLogger(type);
}
#endregion
#region Exception handling on App Domain
protected override void OnUnhandledException(object sender,
System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
{
if (_doHandle)
{
Log.Error(e.Exception.InnerException);
_msgBox.ShowException(e.Exception.InnerException);
e.Handled = true;
}
else
{
Log.Error(e.Exception.InnerException);
_msgBox.ShowException(e.Exception);
e.Handled = false;
}
}
#endregion
When I run app and throw exception from view modle class it show message box that is ok but exception is not logged to file.
I try log exception in view model calls:
something like this: Log.Error(new Exception("4"));
This work, but If i try log exception in OnUnhandleException method it doesnt wokr. Why?
Your problem is that the static field Log gets initialized before your static constructor runs see (Static field initialization). So you will have your Log field with the default Caliburn Nulloger initialized instead of your NLogLogger. You should move the Log field initialization into your static constructor.
private static readonly ILog Log;
static MefBootStrapper()
{
LogManager.GetLog = type => new NLogLogger(type);
Log = LogManager.GetLog(typeof(NLogLogger));
}
Hi I try implement solution from this site im my WPF app for global exception handling.
http://www.codeproject.com/Articles/90866/Unhandled-Exception-Handler-For-WPF-Applications.aspx
I use Caliburn Micro as MVVM framework. Service I have in external assembly and it is injected in view model class with MEF.
Here is my implementation for global exception handling in WPF app.
App.xaml
DispatcherUnhandledException="Application_DispatcherUnhandledException"
Startup="Application_Startup"
App class:
public partial class App : Application
{
private IMessageBox _msgBox = new MessageBoxes.MessageBoxes();
public bool DoHandle { get; set; }
private void Application_Startup(object sender, StartupEventArgs e)
{
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
}
private void Application_DispatcherUnhandledException(object sender,
System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
{
if (DoHandle)
{
_msgBox.ShowException(e.Exception);
e.Handled = true;
}
else
{
_msgBox.ShowException(e.Exception);
e.Handled = false;
}
}
void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
var ex = e.ExceptionObject as Exception;
_msgBox.ShowException(ex);
}
}
Service method from external assembly:
public void ServiceLogOn()
{
try
{
}
catch (Exception ex)
{
throw ex;
}
}
This service method is call in view model class for example on button click event:
[Export(typeof(ILogOnViewModel))]
public class LogOnViewModel : Screen, ILogOnViewModel
{
public void LogOn()
{
_service.ServiceLogOn();
}
}
I run WPF app in Visual Studio and produce exception with message "Bad credentials" in ServiceLogOn method.
I expect that I see the message box with the exception.
But Visual Studio stop debuging app and show exception in service method in service project.
So I try run WPF from exe file and produce same exception in ServiceLogOn method.
I get this error:
Exception has been throw by target of an invocation.
Any exception from view model class is not handled in methods:
Application_DispatcherUnhandledException
or CurrentDomain_UnhandledException.
in App class.
What I do bad?
EDITED with Simon Fox’s answer.
I try implement in MEF bootstraper advice of Simon Fox’s, but I still something do wrong.
I move handle logic for exception to OnUnhandledException method in bootstraper class.
Here is my code from bootstraper class:
public class MefBootStrapper : Bootstrapper<IShellViewModel>
{
//...
private IMessageBox _msgBox = new MessageBoxes.MessageBoxes();
public bool DoHandle { get; set; }
protected override void OnUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
{
if (DoHandle)
{
_msgBox.ShowException(e.Exception);
e.Handled = true;
}
else
{
_msgBox.ShowException(e.Exception);
e.Handled = false;
}
}
//...
}
I bind some method from view model on button and throw new exception. Something like this:
public void LogOn()
{
throw new ArgumentException("Bad argument");
}
But result is sam, I test app out of Visual Studio and get this exception.
Exception has been throw by target of an invocation.
Caliburn.Micro has built in support for hooking unhandled exceptions. The Bootstrapper class (which every Caliburn project requires) sets this up for you and provides the virtual OnUnhandledException method.
In your custom BootStrapper you must override OnUnhandledException to perform any custom actions for unhandled exceptions in your app. Note that you will most likely have to marshal actions such as displaying a message box to the UI thread (Caliburn enables this easily via Execute.OnUIThread).
You may also have an issue in the way your service moves exceptions to the client, but without any details of how the service is implemented/hosted/etc I cannot help. Are you using WCF to do SOAP? Are you using FaultContracts?