UnhandledException Handler

  • Thread starter Thread starter Thom Little
  • Start date Start date
T

Thom Little

I have a simple C# Winforms application that has an UnhandledException
handler that writes to the log. This works fine if I throw a new exception
(like"bogus") but it does not gain control on a Zero Divide exception.

Is the UnhandledException handler supposed to receive control on all
exceptions or is the UnhandledException hander supposed to receive control
on all exceptions after all other exception handlers have "had a chance" to
service it?

Is the expected behavior that an UnhandledException handler will receive
Zero Divide exceptions or is the expected behavior that an Unhandled
Exception handler will never receive Zero Divide exceptions?
 
Thom Little said:
I have a simple C# Winforms application that has an UnhandledException
handler that writes to the log. This works fine if I throw a new exception
(like"bogus") but it does not gain control on a Zero Divide exception.

Is the UnhandledException handler supposed to receive control on all
exceptions or is the UnhandledException hander supposed to receive control
on all exceptions after all other exception handlers have "had a chance" to
service it?

Is the expected behavior that an UnhandledException handler will receive
Zero Divide exceptions or is the expected behavior that an Unhandled
Exception handler will never receive Zero Divide exceptions?

The UnhandledExceptionHandler never gets to see the "Attempted to divide by zero" because the exception IS being handled by the
"Application" class. The dialog box you see is a result of the System.Windows.Forms.NativeWindow.OnThreadException method
executing. A catch block in the Application.Run method is responsible for running it in response to encountering a
System.Exception-derived exception (this behaviour is disabled when the debugger is attached).

To override this behaviour, see a

http://msdn.microsoft.com/library/d...readingthreadexceptioneventargsclasstopic.asp

and code below.
Other problems related to that Exception Dialog can relate to the
<system.windows.forms jitDebugging="true" />

setting in the machine.config or app.config

http://msdn.microsoft.com/library/d...blingjust-in-timedebuggingforwindowsforms.asp


and to the setup of the HKEY_LOCAL_MACHINE\Software\Microsoft\.NETFramework\DbgJITDebugLaunchSetting Registry key
http://msdn.microsoft.com/library/d...ide/html/cpconenablingjit-attachdebugging.asp


using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Diagnostics;
using System.Threading;

namespace Unhandled {

public class UnhandledExceptionTest : System.Windows.Forms.Form {

private System.Windows.Forms.Button btnThrow;
private System.ComponentModel.Container components = null;

public UnhandledExceptionTest() {
InitializeComponent();
}

protected override void Dispose( bool disposing ) {
if( disposing ) {
if (components != null) {
components.Dispose();
}
}
base.Dispose( disposing );
}

#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.btnThrow = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// btnThrow
//
this.btnThrow.Location = new System.Drawing.Point(104, 24);
this.btnThrow.Name = "btnThrow";
this.btnThrow.TabIndex = 0;
this.btnThrow.Text = "&Throw";
this.btnThrow.Click += new System.EventHandler(this.btnThrow_Click);
//
// UnhandledExceptionTest
//
this.AutoScaleBaseSize = new System.Drawing.Size(6, 15);
this.ClientSize = new System.Drawing.Size(320, 65);
this.Controls.Add(this.btnThrow);
this.Name = "UnhandledExceptionTest";
this.Text = "UnhandledExceptionTest";
this.ResumeLayout(false);

}
#endregion

[STAThread]
static void Main() {

// Create AppDomain object
AppDomain adCurrent = AppDomain.CurrentDomain;

// Attach the UnhandledExceptionEventHandler to
// the UnhandledException of the APPDOMAIN object
adCurrent.UnhandledException += new UnhandledExceptionEventHandler(
UnhandledExceptionTest.UnhandledExceptionHandler
);

// Create an instance of the class with the methods
// that will handle the exception.
CustomExceptionHandler eh = new CustomExceptionHandler();

// Add the event handler to the APPLICATION object event.
// This will replace the default Exception dialog box
// shown by NativeWindow.OnThreadException
Application.ThreadException += new ThreadExceptionEventHandler(eh.OnThreadException);

Application.Run(new UnhandledExceptionTest());
}

// This will only catch unhandled exceptions NOT handled
// by System.Windows.Forms.NativeWindow.OnThreadException method.
private static void UnhandledExceptionHandler(
object sender,
UnhandledExceptionEventArgs ue
){
CustomExceptionHandler.Log.WriteEntry(
((Exception)ue.ExceptionObject).Message,
EventLogEntryType.Error
);
Application.Exit(); // Think twice before you just keep going
}

private void btnThrow_Click(object sender, System.EventArgs e) {
int a = 1;
int b = 10/(--a);
}

} // End class UnhandledExceptionTest

// Based on MSDN ThreadExceptionEventArgs Class code example
// Creates a class to handle the exception event.
internal class CustomExceptionHandler {
// Handles the exception event
public void OnThreadException(object sender, ThreadExceptionEventArgs t) {
//
// Do whatever needs to be done here
//
string errorMsg =
"An error occurred please contact the adminstrator "
+ "with the following information:\n\n"
+ t.Exception.Message
+ "\n\nStack Trace:\n"
+ t.Exception.StackTrace;

Log.WriteEntry( errorMsg, EventLogEntryType.Error );

Application.Exit(); // Think twice before you just keep going
}

private static EventLog eventLog_ = null;

private const string logName = "Application";
private const string sourceName = "UnhandledExceptionTest";
private const string machineName = ".";

public static EventLog Log {
get {
if (null != eventLog_) return eventLog_;

if( ! EventLog.SourceExists(sourceName, machineName) )
EventLog.CreateEventSource( sourceName, logName, machineName );

eventLog_ = new EventLog( logName, machineName, sourceName );
return eventLog_;
}
}
} // end class CustomExceptionHandler
} // end namespace Unhandled
 
It now makes sense.

Page 254 of the book "70-316, MCAD/MCSD Developing and Implementing
Windows-based Applications with Visual C# .NET and Visual Studio .NET
Training Guide" (Que) is apparently in error.

Reworking it with the information you provided produced the desired
results.

Thank you.
 
Thom Little said:
It now makes sense.

Page 254 of the book "70-316, MCAD/MCSD Developing and Implementing
Windows-based Applications with Visual C# .NET and Visual Studio .NET
Training Guide" (Que) is apparently in error.

Well, it could have been better. If you execute it with debug->Start everything seems to work fine as the Application.Run try block
is disabled; first you are notified when the exception occurs and if you select break and step through, the
UnhandledExceptionEventHandler will get the exception. Run the program with "Debug->Start Without Debugging" or create a release
version and the behaviour changes.

The UnhandledExceptionEventHandler should catch all of your exceptions, as your exceptions should be derived from
System.ApplicationException or one of its derived classes - I believe Application.Run will leave those alone
(it looks for System.SystemException and its descendents).
SystemExceptions should be handled at the site whenever possible, otherwise it is unlikely that you can recover from them.
But it seems that in windows forms you need to subscribe to both AppDomain.CurrentDomain.UnhandledException and
Application.ThreadException to perform application critical cleanup and logging.

I found this information in Jeffrey Richter's "Applied Microsoft .NET Framework Programming"
Chapter 18 "Exceptions" - "Unhandled Exceptions and Windows Forms"
 
Thank you AGAIN for even more helpful information.

F5 - Run with debugging - traps the exception
Ctrl-F5 - Run without debugging - does not trap the exception

--
-- Thom Little -- www.tlaNET.net -- Thom Little Associates, Ltd.
--

UAError said:
Well, it could have been better. If you execute it with debug->Start
everything seems to work fine as the Application.Run try block
is disabled; first you are notified when the exception occurs and if you
select break and step through, the
UnhandledExceptionEventHandler will get the exception. Run the program
with "Debug->Start Without Debugging" or create a release
version and the behaviour changes.

The UnhandledExceptionEventHandler should catch all of your exceptions, as
your exceptions should be derived from
System.ApplicationException or one of its derived classes - I believe
Application.Run will leave those alone
(it looks for System.SystemException and its descendents).
SystemExceptions should be handled at the site whenever possible,
otherwise it is unlikely that you can recover from them.
But it seems that in windows forms you need to subscribe to both
AppDomain.CurrentDomain.UnhandledException and
 
Back
Top