Global exception catch works only while debugging the WinForms application - c#

I implemented the following global exception catch in my WinForms application:
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
try
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MyMainForm());
}
catch (Exception ex)
{
MessageBox.Show("UNHANDLED EXCEPTION: The program will be terminated. Details follow:\n\n" +
getExceptionInfoWithDebuggerOutput(ex),
"Global Exception",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
}
}
Then down the code, an exception is raised (something as this one -- totally due to my forgetfulness):
public partial class MyPage : UserControl
{
void func1()
{
SqlConnectionStringBuilder conStr = null;
//... later
conStr.DataSource = strServer; //<<--- Where exception is raised
}
}
Now, if I'm debugging my project, I see my Global Exception message box from the global exception handler.
But if I'm not debugging my project and run it as Ctrl+F5, or if I build a Release project, I get the following window instead of the one I coded above:
Any idea how to make my global exception handler do the processing instead?

You should be hooking an event such as AppDomain.UnhandledException.
These events are raised before the global error handler you're seeing in release mode. This allows you to log errors before bailing out.. in a nicer way.
There are other events that are raised also. For example, Application.ThreadException. Reading the documentation will give you better insights into your specific needs.
I have to note that the error you're seeing is a NullReferenceException.. which would ideally be nicely handled within your code. Still, hooking these events and logging exceptions is a good idea.

Related

Can C# WinForm static void Main NOT catching Exception?

I have a WinForm application written in C# where I put a try-catch block in the Program.cs, in the program entry, the static void Main method, right in the beginning of the application like this:
using System;
using System.IO;
using System.Windows.Forms;
namespace T5ShortestTime {
static class Program {
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main() {
try {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new T5ShortestTimeForm());
} catch (Exception e) {
string errordir = Path.Combine(Application.StartupPath, "errorlog");
string errorlog = Path.Combine(errordir, DateTime.Now.ToString("yyyyMMdd_HHmmss_fff") + ".txt");
if (!Directory.Exists(errordir))
Directory.CreateDirectory(errordir);
File.WriteAllText(errorlog, e.ToString());
}
}
}
}
As you can see, the Application is put in a try-catch block and in the catch block, the only thing it does is to create an error log file.
Now, so far so good. My application is running well and if I encounter a crash, the last Exception should be captured by the try-catch block and stored in the error log file.
However, as I run my program for a while, I get an unhandled exception (null reference). What surprise me is that the exception does not create an error log file.
Now, this post shows that it is possibly caused by ThreadException or HandleProcessCorruptedStateExceptions (the two most upvoted answers), but my case shows a simple null reference exception:
Problem signature:
Problem Event Name: CLR20r3
Problem Signature 01: T5ShortestTime.exe
Problem Signature 02: 2.8.3.1
Problem Signature 03: 5743e646
Problem Signature 04: T5ShortestTime
Problem Signature 05: 2.8.3.1
Problem Signature 06: 5743e646
Problem Signature 07: 182
Problem Signature 08: 1b
Problem Signature 09: System.NullReferenceException
OS Version: 6.3.9600.2.0.0.272.7
Locale ID: 1033
Additional Information 1: bb91
Additional Information 2: bb91a371df830534902ec94577ebb4a3
Additional Information 3: aba1
Additional Information 4: aba1ed7202d796d19b974eec93d89ec2
Read our privacy statement online:
http://go.microsoft.com/fwlink/?linkid=280262
If the online privacy statement is not available, please read our privacy statement offline:
C:\Windows\system32\en-US\erofflps.txt
Why would that be?
the last Exception should be captured by the try-catch block
That is not going to happen. Except in one case, when you run your program with a debugger attached. So you surely got lulled into believing it would work, everybody always starts out running their program with F5 for a while.
Application.Run() has a back-stop in its code that raises events, try/catch-em-all that raises the Application.ThreadException event when an event handler throws an unhandled exception. That back-stop is really, really necessary, especially on the x64 version of Windows 7. Very Bad Things happen when there is no exception handler. That back-stop is however not in place when you run with the debugger, that makes unhandled exceptions too difficult to debug.
So when you debug then your catch clause will run. Making unhandled exceptions too difficult to debug. When you run without a debugger then your catch clause will not run and your program will crash, just as you described. Making unhandled exception too difficult to debug.
So don't do it this way. How Application.Run() deals with unhandled exceptions is configured with the Application.SetUnhandledExceptionMode() method. You'll like this version better:
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
if (!System.Diagnostics.Debugger.IsAttached) {
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException);
AppDomain.CurrentDomain.UnhandledException += LogException;
}
Application.Run(new Form1());
}
private static void LogException(object sender, UnhandledExceptionEventArgs e) {
string errordir = Path.Combine(Application.StartupPath, "errorlog");
string errorlog = Path.Combine(errordir, DateTime.Now.ToString("yyyyMMdd_HHmmss_fff") + ".txt");
if (!Directory.Exists(errordir))
Directory.CreateDirectory(errordir);
File.WriteAllText(errorlog, e.ToString());
AppDomain.CurrentDomain.UnhandledException -= LogException;
MessageBox.Show("Error details recorded in " + errorlog, "Unexpected error");
Environment.Exit(1);
}
With this code in place, you can debug unhandled exceptions without any problems. The Debugger.IsAttached test ensures that the debugger will always stop when an event handler falls over. Without a debugger, it then disables the Application.ThreadException event (it is quite useless) and favors listening to all exceptions. Including the ones raised in worker threads.
You ought to give an alert to the user so the window doesn't just disappear without any trace. I was going to recommend MessageBox but noticed that this bug is currently back again on Windows 10. Sigh.
ThreadException is not an exception type like (NullReferenceException). It is that:
This event allows your Windows Forms application to handle otherwise
unhandled exceptions that occur in Windows Forms threads
This means that it handles exceptions in threads other than the Main Thread.
So, you need to subscribe to : AppDomain.CurrentDomain.UnhandledException also in order to handle the exceptions in your Main Thread (Regardless of the type of the exception e.g. NullReference, IndexOutOfRange, etc..).
Ok, in the end I implement Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException) as exampled by Hans Passant for VB.Net in this post. Here I put my own code + error logging for C#:
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
if (!System.Diagnostics.Debugger.IsAttached) {
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException);
AppDomain.CurrentDomain.UnhandledException += LogUnhandledExceptions;
}
Application.Run(new T5ShortestTimeForm());
}
private static void LogUnhandledExceptions(object sender, UnhandledExceptionEventArgs e) {
Exception ex = (Exception)e.ExceptionObject;
string errordir = Path.Combine(Application.StartupPath, "errorlog");
string errorlog = Path.Combine(errordir, DateTime.Now.ToString("yyyyMMdd_HHmmss_fff") + ".txt");
if (!Directory.Exists(errordir))
Directory.CreateDirectory(errordir);
File.WriteAllText(errorlog, ex.ToString());
Environment.Exit(System.Runtime.InteropServices.Marshal.GetHRForException(ex));
}
Also, it seems like the source of confusion here is that there are actually two propagated Exceptions occur:
The first one was any Exception from the application itself:
System.Exception: Exception of type 'System.Exception' was thrown.
at T5ShortestTime.T5ShortestTimeForm..ctor() in C:\Test.cs:line 45
at T5ShortestTime.Program.Main() in C:\Test.cs:line 19
at ...
And the second one occurs during the Dispose of the Form components, which creates another exception, and it is the null reference exception:
System.NullReferenceException: Object reference not set to an instance of an object.
at T5ShortestTime.T5ShortestTimeForm.Dispose(Boolean disposing)
at System.ComponentModel.Component.Finalize()
So, when I test the exception in my app, the NullReferenceException comes the last, in the Dispose.
I only manage to capture this after I set the UnhandledExceptionMode to ThrowException above.

Replacing the standard exception/error form in .net

I was thinking, is it possible to "override" or maybe replace the standard winforms .net Exception form??
I am talking about this dude:
Is it possible, during the scope of the hole program, to override this windows? I mean automatically, without having to perform try/catch.
You can handle System.Windows.Forms.Application.ThreadException event to show your custom message.
You should handle both the following events:
AppDomain.CurrentDomain.UnhandledException
Application.ThreadException
In those handlers you could display a custom form that you made.
This is the ThreadExceptionDialog class, it derives from the Form class. Deriving from it to alter the dialog is a lost cause, you can't easily get to the embedded controls. You can create your own Form derived class to make your own dialog just as well, just give it a constructor that takes an Exception argument. Implement an event handler for Application.ThreadException to display it.
Do note the fundamental flaw in the dialog. It expects the user to make the right choice when she needs to click a button to dismiss the dialog. With, in general, fairly obscure information about what exactly went wrong. It means something to you, rarely anything more than 'oh crap' to the user. Clicking the Continue button is not typically the right thing to do.
To avoid the user having to make such a difficult choice, call Application.SetUnhandledExceptionMode() in your Main() method, passing ThrowException so that the event is never raised. Every unhandled exception now goes through AppDomain.UnhandledException. Including the ones that were raised in a worker thread, exceptions that don't produce the dialog. Write an event handler for it and display and/or log the value of e.ExceptionObject.ToString(). It is up to you to sort out a way to get that info to your desk, or the user's IT staff, so you can improve your product and they can get their machines stable.
In webforms, you can handle Application_Error in global.asax. But it seems like you're talking about winforms. In that case:
try adding the following code into your main starter method:
Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(ExceptionHandler.OnThreadException);
And define the ExceptionHandler class as follows:
/// <summary>
/// Main thread error handler.
/// </summary>
public sealed class ExceptionHandler
{
private ExceptionHandler()
{}
/// <summary>
/// Handles an exception on the main thread.
/// </summary>
/// <param name="sender"></param>
/// <param name="t"></param>
public static void OnThreadException(object sender, ThreadExceptionEventArgs t)
{
DialogResult result = DialogResult.Cancel;
try
{
result = ShowThreadExceptionDialog(t.Exception);
}
catch
{
try
{
MessageBox.Show("Fatal Error", "Application Error", MessageBoxButtons.OK, MessageBoxIcon.Stop);
}
finally
{
Application.Exit();
}
}
// Exits the program when the user clicks Abort.
if (result == DialogResult.Abort)
Application.Exit();
}
// Creates the error message and displays it.
private static DialogResult ShowThreadExceptionDialog(Exception e)
{
string errorMsg = "An error occurred please contact the adminstrator with the following information:\n\n";
errorMsg = errorMsg + e.Message + "\n\nStack Trace:\n" + e.StackTrace;
return MessageBox.Show(errorMsg, "Application Error", MessageBoxButtons.AbortRetryIgnore, MessageBoxIcon.Stop);
}
}
taken from: http://www.eggheadcafe.com/community/aspnet/2/27469/any-method-for-handling-error-globally-in-c.aspx
Check this link.
From the page:
Introduction
One of the things that impressed me when I first started learning .NET was its enhanced exception-handling functionality. By this I mean such features as easy access to the type of exception thrown, full stack trace and inner exceptions. This makes it easy to still get full information in those places where you just catch a top-level System.Exception. I find this convenient since, if you don't need to take specific action on a particular type of exception, it is tedious to have successive catch handlers for each type of exception that may be thrown. In any case, even if you do catch specific exceptions you usually also need to catch System.Exception just to cover yourself and prevent program crashes. Thus I find that I end up catching System.Exception all over the place in my code. A typical scenario is that in Windows Forms and ASP.NET Web Forms applications, all of my non-trivial event handlers end up containing try-catch System.Exception blocks.
The trouble is that this does still clutter up the code somewhat and doesn't really seem quite right. Is there a better way?

Catching exception in Main() method

Consider the following simple application: a windows form created by a "new C# windows application" sequence in VS that was modified in a following way:
public static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
try
{
Application.Run(new Form1());
}
catch (Exception ex)
{
MessageBox.Show("An unexpected exception was caught.");
}
}
Form1.cs contains the following modifications:
private void Form1_Load(object sender, EventArgs e)
{
throw new Exception("Error");
}
If I press F5 in IDE, then, as I expect, I see a message box saying that exception was caught and the application quits.
If I go to Debug(or Release)/bin and launch the executable, I see the standard "Unhandled exception" window, meaning that my exception handler doesn't work.
Obviously, that has something to do with exception being thrown from a different thread that Application.Run is called from. But the question remains - why the behavior differs depending on whether the application has been run from IDE or from command line?
What is the best practice to ensure that no exceptions remain unhandled in the application?
Normally Application.ThreadException will handle the exception in the Load event. You'll get the ThreadExceptionDialog that offers the Quit and Continue options.
But not when a debugger is attached. The catch clause in the message loop that displays the dialog is intentionally disabled in that case. That's necessary because it would be very difficult to trouble-shoot exceptions if that dialog pops up when you debug a program. Which this catcher no longer active, your catch clause in the Main() method now gets a shot at the exception.
You can make it consistent by using Application.SetUnhandledExceptionMode() in the Main() method. You shouldn't, exceptions really are hard to debug if you do this. If you want to customize exception handling for the UI thread then you should register your own Application.ThreadException handler:
if (!System.Diagnostics.Debugger.IsAttached)
Application.ThreadException += myThreadException;
Trapping unhandled exceptions in worker threads requires a AppDomain.UnhandledException handler. They are not recoverable.
Also beware of a bug in 64-bit Windows, exceptions in the Load event are swallowed without diagnostic when a debugger is attached. Force AnyCPU mode to avoid that trap.
In addition to catching any exceptions thrown inside the Main method you must also handle AppDomain.CurrentDomain.UnhandledException and Application.ThreadException.
Not sure why the behavior differs with and without a debugger attached though.

c# console application - prevent default exception dialog

I have simple application with single AppDomain which is periodicaly launched on a server. Sometimes unhandled exception occurs in the aplication and default abort/retry/ignore dialog pops up. I need to somehow prevent the edialog from showing and just output the exception on StrErr and close the application. So I enclosed all the code in main method with try-catch statement, but it didn't help at all - the exception dialog is still shown sometimes.
The Main() code looks like this:
try
{
RunApplication();
}
catch (Exception exc)
{
Console.Error.WriteLine(exc.ToString());
Console.Error.WriteLine(exc.StackTrace);
if (exc.InnerException != null)
{
Console.Error.WriteLine(exc.InnerException.ToString());
Console.Error.WriteLine(exc.InnerException.StackTrace);
}
Environment.Exit(666);
}
This try-catch clause shoud catch all unhandled exceptions and the exception dialog should never popup AFAIK. Am I missing something? Or is there any setting (registry etc) on the server which controls some special behaviour related to the exception dialog/application error code?
There's an unhandled exception event you can subscribe to in the application domain.
public static void Main()
{
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(OnUnhandledException);
//some code here....
}
/// <summary>
/// Occurs when you have an unhandled exception
/// </summary>
public static void OnUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
//here's how you get the exception
Exception exception = (Exception)e.ExceptionObject;
//bail out in a tidy way and perform your logging
}
Have you considered the possibility that your catch clause may be throwing exceptions?
Do you spawn threads in your main app?

Globally catch exceptions in a WPF application?

We have a WPF application where parts of it may throw exceptions at runtime. I'd like to globally catch any unhandled exceptions and log them, but otherwise continue program execution as if nothing happened (kinda like VB's On Error Resume Next).
Is this possible in C#? And if so, where exactly would I need to put the exception handling code?
Currently I can't see any single point where I could wrap a try/catch around and which would catch all exceptions that could occur. Even then, I would have left whatever has been executed because of the catch. Or am I thinking in horribly wrong directions here?
ETA: Because many people below pointed it out: The application is not for controlling nuclear power plants. If it crashes, it's not that big a deal, but it throws random exceptions that are mostly UI-related that are a nuisance in the context where it would be used. There were (and probably still are) a few of those and since it uses a plugin architecture and may be extended by others (also students in that case; so no experienced developers that are able to write completely error-free code).
As for the exceptions that get caught: I do log them to a log file, including the complete stack trace. That was the whole point of that exercise. Just to counter those people that were taking my analogy to VB's OERN too literally.
I know that blindly ignoring certain classes of errors is dangerous and might corrupt my application instance. As said before, this program isn't mission-critical for anyone. No-one in their right mind would bet the survival of the human civilization on it. It's simply a little tool for testing certain design approaches wrt. software engineering.
For the immediate use of the application there are not many things that can happen on an exception:
No exception handling – error dialog and application exit. Experiment has to be repeated, though likely with another subject. No errors have been logged, which is unfortunate.
Generic exception handling – benign error trapped, no harm done. This should be the common case judged from all errors we were seeing during development. Ignoring this kind of errors should have no immediate consequences; the core data structures are tested well enough that they will easily survive this.
Generic exception handling – serious error trapped, possibly crash at a later point. This may happen rarely. We've never seen it so far. The error is logged anyway and a crash might be inevitable. So this is conceptually similar to the very first case, except that we have a stack trace. And in the majority of cases the user won't even notice.
As for the experiment data generated by the program: A serious error would at worst just cause no data to be recorded. Subtle changes that change the result of the experiment ever so slightly are pretty unlikely. And even in that case, if the results seem dubious the error was logged; one can still throw away that data point if it's a total outlier.
To summarize: Yes, I consider myself still at least partially sane and I don't consider a global exception handling routine which leaves the program running to be necessarily totally evil. As said twice before, such a decision might be valid, depending on the application. In this case it was judged a valid decision and not total and utter bullshit. For any other application that decision might look different. But please don't accuse me or the other people who worked on that project to potentially blow up the world just because we're ignoring errors.
Side note: There is exactly one user for that application. It's not something like Windows or Office that gets used by millions where the cost of having exceptions bubble to the user at all would be very different in the first place already.
Use the Application.DispatcherUnhandledException Event. See this question for a summary (see Drew Noakes' answer).
Be aware that there'll be still exceptions which preclude a successful resuming of your application, like after a stack overflow, exhausted memory, or lost network connectivity while you're trying to save to the database.
Example code using NLog that will catch exceptions thrown from all threads in the AppDomain, from the UI dispatcher thread and from the async functions:
App.xaml.cs :
public partial class App : Application
{
private static Logger _logger = LogManager.GetCurrentClassLogger();
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
SetupExceptionHandling();
}
private void SetupExceptionHandling()
{
AppDomain.CurrentDomain.UnhandledException += (s, e) =>
LogUnhandledException((Exception)e.ExceptionObject, "AppDomain.CurrentDomain.UnhandledException");
DispatcherUnhandledException += (s, e) =>
{
LogUnhandledException(e.Exception, "Application.Current.DispatcherUnhandledException");
e.Handled = true;
};
TaskScheduler.UnobservedTaskException += (s, e) =>
{
LogUnhandledException(e.Exception, "TaskScheduler.UnobservedTaskException");
e.SetObserved();
};
}
private void LogUnhandledException(Exception exception, string source)
{
string message = $"Unhandled exception ({source})";
try
{
System.Reflection.AssemblyName assemblyName = System.Reflection.Assembly.GetExecutingAssembly().GetName();
message = string.Format("Unhandled exception in {0} v{1}", assemblyName.Name, assemblyName.Version);
}
catch (Exception ex)
{
_logger.Error(ex, "Exception in LogUnhandledException");
}
finally
{
_logger.Error(exception, message);
}
}
AppDomain.UnhandledException Event
This event provides notification of uncaught exceptions. It allows the
application to log information about the exception before the system
default handler reports the exception to the user and terminates the
application.
public App()
{
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.UnhandledException += new UnhandledExceptionEventHandler(MyHandler);
}
static void MyHandler(object sender, UnhandledExceptionEventArgs args)
{
Exception e = (Exception) args.ExceptionObject;
Console.WriteLine("MyHandler caught : " + e.Message);
Console.WriteLine("Runtime terminating: {0}", args.IsTerminating);
}
If the UnhandledException event is handled in the default application
domain, it is raised there for any unhandled exception in any thread,
no matter what application domain the thread started in. If the thread
started in an application domain that has an event handler for
UnhandledException, the event is raised in that application domain. If
that application domain is not the default application domain, and
there is also an event handler in the default application domain, the
event is raised in both application domains.
For example, suppose a thread starts in application domain "AD1",
calls a method in application domain "AD2", and from there calls a
method in application domain "AD3", where it throws an exception. The
first application domain in which the UnhandledException event can be
raised is "AD1". If that application domain is not the default
application domain, the event can also be raised in the default
application domain.
In addition what others mentioned here, note that combining the Application.DispatcherUnhandledException (and its similars) with
<configuration>
<runtime>
<legacyUnhandledExceptionPolicy enabled="1" />
</runtime>
</configuration>
in the app.config will prevent your secondary threads exception from shutting down the application.
Here is complete example using NLog
using NLog;
using System;
using System.Windows;
namespace MyApp
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
private static Logger logger = LogManager.GetCurrentClassLogger();
public App()
{
var currentDomain = AppDomain.CurrentDomain;
currentDomain.UnhandledException += CurrentDomain_UnhandledException;
}
private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
var ex = (Exception)e.ExceptionObject;
logger.Error("UnhandledException caught : " + ex.Message);
logger.Error("UnhandledException StackTrace : " + ex.StackTrace);
logger.Fatal("Runtime terminating: {0}", e.IsTerminating);
}
}
}
in Wpf application Project follow this Steps:
in App.xaml.cs file:
using 'System.Windows.Threading'
create App_DispatcherUnhandledException method same example.
example:
using System.Windows;
using System.Windows.Threading;
namespace Test
{
public partial class App : Application
{
void App_DispatcherUnhandledException(object sender,
DispatcherUnhandledExceptionEventArgs e)
{
// Process unhandled exception
// Prevent default unhandled exception processing
e.Handled = true;
}
}
}
in App.xaml:
add DispatcherUnhandledException="App_DispatcherUnhandledException"
for Example:
<Application x:Class="eValGr.UI.Light.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:eValGr.UI.Light"
DispatcherUnhandledException="Application_DispatcherUnhandledException">
<Application.Resources>
</Application.Resources>
</Application>
Like "VB's On Error Resume Next?" That sounds kind of scary. First recommendation is don't do it. Second recommendation is don't do it and don't think about it. You need to isolate your faults better. As to how to approach this problem, it depends on how you're code is structured. If you are using a pattern like MVC or the like then this shouldn't be too difficult and would definitely not require a global exception swallower. Secondly, look for a good logging library like log4net or use tracing. We'd need to know more details like what kinds of exceptions you're talking about and what parts of your application may result in exceptions being thrown.

Categories