confused with Application.ThreadException

L

Lloyd Dupont

I'm using 2.0 beta 2, that might be the source of the problem.

Anyway at the start of my application I register a global exception handler:
Application.ThreadException += someGlobalHandler;

anyway, wether I throw an exception in an auxiliary thread or in the main
GUI thread, the global handler seems never to be called ...... ?!!?

Is this method deprecated?
Isn't a way to catch all?

I know I could try { Application.Run() } catch {}
But I found this method quite handy.. :-/
 
N

Nick Hertl

Lloyd,

At first, I too was confused by this after trying a console application
where I added this event handler and nothing happened. So I did some
research and got the details.

This event is actually used only for Winforms where they would
otherwise use behaviour that you may not like. If you write a winforms
app with a button_click or other type of event handler, and then that
handler throws an unhandled exception, winforms will usually just pop
up a dialog to tell you about it and allow you to continue.

If you want to override this behavior, you can register a handler for
the Application.ThreadException event and do whatever you like instead.

For an example of what I mean, play around with this application:

----------------------
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Threading;

namespace Example
{
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.Run(new Form1());
}
}

partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private void button1_Click(object sender, EventArgs e)
{
throw new Exception();
}

private void checkbox1_CheckChanged(object sender, EventArgs e)
{
if(checkbox1.Checked)
{
Application.ThreadException += myhandler;
this.Text = "Handler On";
}
else
{
Application.ThreadException -= myhandler;
this.Text = "Handler Off";
}
button1.Text = "Click Here";
}

private void handler(object sender, ThreadExceptionEventArgs e)
{
button1.Text = "In handler";
}
}

partial class Form1
{
private Button button1;
private CheckBox checkbox1;
private ThreadExceptionEventHandler myhandler;
private System.ComponentModel.IContainer components = null;
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
private void InitializeComponent()
{
this.button1 = new Button();
this.checkbox1 = new CheckBox();
this.SuspendLayout();
this.button1.Location = new Point(40, 32);
this.button1.Name = "button1";
this.button1.TabIndex = 0;
this.button1.Text = "Click Here";
this.button1.Click += new EventHandler(this.button1_Click);
this.checkbox1.Location = new Point(40, 64);
this.checkbox1.Name = "checkbox1";
this.checkbox1.TabIndex = 1;
this.checkbox1.CheckedChanged += new
EventHandler(this.checkbox1_CheckChanged);
this.ClientSize = new Size(292, 273);
this.Controls.Add(this.button1);
this.Controls.Add(this.checkbox1);
this.Name = "Form1";
this.Text = "Handler Off";
this.ResumeLayout(false);
myhandler = new ThreadExceptionEventHandler(handler);
}
}
}
-----------------------------

But be sure to do it outside the debugger because inside the debugger,
Winforms will not pop up that dialog, but rather just let the debugger
tell you about it instead.

Alternatively, if you are interested in any Unhandled exception that
occurs in your application, you could hook the
AppDomain.UnhandledException event like this instead:

--------------
using System;

class Program
{
static void Main(string[] args)
{
AppDomain.CurrentDomain.UnhandledException += new
UnhandledExceptionEventHandler(handler);
throw new Exception();
}

static void handler(object sender, UnhandledExceptionEventArgs e)
{
Console.WriteLine("In Handler");
}
}
---------

Once you receive this callback however, you are too late to continue
letting your app run normally.

In early stages of Whidbey there were other similar events like
ExceptionThrown and ExceptionCaught, but they have since ben removed.

I hope this information is useful to you.
 
L

Lloyd Dupont

Hi Nick!

Very interesting answer ;-)
Thanks for that.

--
If you're in a war, instead of throwing a hand grenade at the enemy, throw
one of those small pumpkins. Maybe it'll make everyone think how stupid war
is, and while they are thinking, you can throw a real grenade at them.
Jack Handey.
Nick Hertl said:
Lloyd,

At first, I too was confused by this after trying a console application
where I added this event handler and nothing happened. So I did some
research and got the details.

This event is actually used only for Winforms where they would
otherwise use behaviour that you may not like. If you write a winforms
app with a button_click or other type of event handler, and then that
handler throws an unhandled exception, winforms will usually just pop
up a dialog to tell you about it and allow you to continue.

If you want to override this behavior, you can register a handler for
the Application.ThreadException event and do whatever you like instead.

For an example of what I mean, play around with this application:

----------------------
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Threading;

namespace Example
{
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.Run(new Form1());
}
}

partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private void button1_Click(object sender, EventArgs e)
{
throw new Exception();
}

private void checkbox1_CheckChanged(object sender, EventArgs e)
{
if(checkbox1.Checked)
{
Application.ThreadException += myhandler;
this.Text = "Handler On";
}
else
{
Application.ThreadException -= myhandler;
this.Text = "Handler Off";
}
button1.Text = "Click Here";
}

private void handler(object sender, ThreadExceptionEventArgs e)
{
button1.Text = "In handler";
}
}

partial class Form1
{
private Button button1;
private CheckBox checkbox1;
private ThreadExceptionEventHandler myhandler;
private System.ComponentModel.IContainer components = null;
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
private void InitializeComponent()
{
this.button1 = new Button();
this.checkbox1 = new CheckBox();
this.SuspendLayout();
this.button1.Location = new Point(40, 32);
this.button1.Name = "button1";
this.button1.TabIndex = 0;
this.button1.Text = "Click Here";
this.button1.Click += new EventHandler(this.button1_Click);
this.checkbox1.Location = new Point(40, 64);
this.checkbox1.Name = "checkbox1";
this.checkbox1.TabIndex = 1;
this.checkbox1.CheckedChanged += new
EventHandler(this.checkbox1_CheckChanged);
this.ClientSize = new Size(292, 273);
this.Controls.Add(this.button1);
this.Controls.Add(this.checkbox1);
this.Name = "Form1";
this.Text = "Handler Off";
this.ResumeLayout(false);
myhandler = new ThreadExceptionEventHandler(handler);
}
}
}
-----------------------------

But be sure to do it outside the debugger because inside the debugger,
Winforms will not pop up that dialog, but rather just let the debugger
tell you about it instead.

Alternatively, if you are interested in any Unhandled exception that
occurs in your application, you could hook the
AppDomain.UnhandledException event like this instead:

--------------
using System;

class Program
{
static void Main(string[] args)
{
AppDomain.CurrentDomain.UnhandledException += new
UnhandledExceptionEventHandler(handler);
throw new Exception();
}

static void handler(object sender, UnhandledExceptionEventArgs e)
{
Console.WriteLine("In Handler");
}
}
---------

Once you receive this callback however, you are too late to continue
letting your app run normally.

In early stages of Whidbey there were other similar events like
ExceptionThrown and ExceptionCaught, but they have since ben removed.

I hope this information is useful to you.
 
R

Richard Grimes [MVP]

Lloyd said:
I'm using 2.0 beta 2, that might be the source of the problem.

Nah, its been part of the framework since the first version.
Anyway at the start of my application I register a global exception
handler: Application.ThreadException += someGlobalHandler;

The clue is in the class. The Application class is used to provide a
link between the Win32 message pump and the event handling mechanism in
Windows Forms. Windows has a restriction (which was in Avalon the last
time I checked) that a Window has thread affinity, so messages for a
window goes to the thread that created the window. The Application class
does all the grungy stuff to make this work (it does this with thread
and application context classes).

When the constructor of a form is called the window is not created.
Instead, the window is created when the Visible property is first set to
true. This is done when Application.Run is called. The handler for the
Visible property will register a Windows class and create the window.
The code that registers the class will use one of two windows
procedures - one is used for when you are running the application under
a debugger. The one used for the case when a debugger is not attached
puts exception handling around the code that handles messages. Hence an
exception thrown by any of your code will be eaten by this exception
handler. To get round this issue the thread context has an even (the
ThreadException event) that is called by this exception handler to give
you an opportunity to handle the exception.

All of this stuff is documented in my Managed C++ book.
anyway, wether I throw an exception in an auxiliary thread or in the
main GUI thread, the global handler seems never to be called ......
?!!?

You need to throw the exception *after* the window is called. If you
call it before the window is created (for example in the constructor)
then the Windows Form object will not be 'attached' to the windows
procedure that I mentioned above.

Richard
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top