Exception Handling Question

C

Chuck Cobb

I'm implementing a centralized exception handling routine using the
Enterprise Library Exception Management Application Block.

I trap all unhandled exceptions to one place using the following method:

// --- Create an Exception Handler for Thread Exceptions ----------------

Application.ThreadException += new
ThreadExceptionEventHandler(OnThreadException);

// --- Create an Exception Handler for Unhandled Exceptions -------------

AppDomain currentDomain = AppDomain.CurrentDomain;

currentDomain.UnhandledException += new
UnhandledExceptionEventHandler(OnUnhandledException);



private static void OnThreadException(object sender,

ThreadExceptionEventArgs t)

//
******************************************************************************

// Handles Normal Thread Exceptions

//
******************************************************************************

{

Exceptions.LogAndReportError(t.Exception);

}



private static void OnUnhandledException(object sender,

UnhandledExceptionEventArgs args)

//
******************************************************************************

// Handles All Unhandled Exceptions

//
******************************************************************************

{

Exceptions.LogAndReportError(args.ExceptionObject as System.Exception);

}

The problem I have is that exceptions that occur on a background thread are
not being picked up. If I do a "throw" on an exception in a background
thread it has no place to go and just disappears as an unhandled exception.
How can I pick up these exceptions without handling them in the background
thread?

Thanks,

Chuck Cobb
 
C

Christoph Nahr

The problem I have is that exceptions that occur on a background thread are
not being picked up. If I do a "throw" on an exception in a background
thread it has no place to go and just disappears as an unhandled exception.
How can I pick up these exceptions without handling them in the background
thread?

What .NET version is that? Joe Duffy's "Professional .NET Framework
2.0" states that Versions 1.0 and 1.1 used to silently swallow
unhandled exceptions on a background thread. Apparently there's
nothing you can do about that. Version 2.0 should correctly invoke
your UnhandledException handler.
 
C

Christoph Nahr

What .NET version is that? Joe Duffy's "Professional .NET Framework
2.0" states that Versions 1.0 and 1.1 used to silently swallow
unhandled exceptions on a background thread. Apparently there's
nothing you can do about that. Version 2.0 should correctly invoke
your UnhandledException handler.

Just found confirmation on Jeff Atwood's weblog, right here:
http://www.codinghorror.com/blog/archives/000201.html

Scroll down to a reply by Jonathan Keljo, CLR Exceptions PM:

"Just to complicate things, in v1.0 and 1.1, an unhandled exception
did not always mean that your application would die. If the unhandled
exception occurred on anything other than the main thread or a thread
that began its life in unmanaged code, the CLR ate the exception and
allowed your app to keep going. This was generally evil, because what
would often happen was, for example, that ThreadPool threads would
silently die off, one by one, until your application wasn't actually
doing any work. Figuring out the cause of this kind of failure was
nearly impossible. [...]"

"In v2.0, an unhandled exception on any thread will take down the
application. We've found that it's tremendously easier to debug
crashes than it is to debug hangs or the silent-stoppage-of-work
problem described above."
 
C

Chuck Cobb

Hi Christopher,

I am using .Net 2.0 and I can confirm that it is still swallowing exceptions
on a background thread. The code I used to test it looks like this

public static void BackgroundProcess
{
try
{
throw new Exception("Simulated Error");
}
catch
{
throw;
}
}

When I execute the above code, it creates the exception, goes to the catch
block and when it attempts to rethrow it using the "throw", it goes nowhere
Christoph Nahr said:
What .NET version is that? Joe Duffy's "Professional .NET Framework
2.0" states that Versions 1.0 and 1.1 used to silently swallow
unhandled exceptions on a background thread. Apparently there's
nothing you can do about that. Version 2.0 should correctly invoke
your UnhandledException handler.

Just found confirmation on Jeff Atwood's weblog, right here:
http://www.codinghorror.com/blog/archives/000201.html

Scroll down to a reply by Jonathan Keljo, CLR Exceptions PM:

"Just to complicate things, in v1.0 and 1.1, an unhandled exception
did not always mean that your application would die. If the unhandled
exception occurred on anything other than the main thread or a thread
that began its life in unmanaged code, the CLR ate the exception and
allowed your app to keep going. This was generally evil, because what
would often happen was, for example, that ThreadPool threads would
silently die off, one by one, until your application wasn't actually
doing any work. Figuring out the cause of this kind of failure was
nearly impossible. [...]"

"In v2.0, an unhandled exception on any thread will take down the
application. We've found that it's tremendously easier to debug
crashes than it is to debug hangs or the silent-stoppage-of-work
problem described above."
 
C

Christoph Nahr

When I execute the above code, it creates the exception, goes to the catch
block and when it attempts to rethrow it using the "throw", it goes nowhere

That's very strange. The UnhandledException handler doesn't get
triggered at all? Does it work if you just throw the exception,
outside of a try/catch block? Maybe the rethrow is the culprit.

In any case, that looks very much like a bug to me. Could you try
isolating a test case and post it here, or post a link to it?
 
W

Willy Denoyette [MVP]

ThreadException is meant to handle unhandled exceptions on the UI thread,
you need to install an UnhandledExceptionEventHandler to handle unhandled
excptions on auxiliary threads.

Willy.

| I'm implementing a centralized exception handling routine using the
| Enterprise Library Exception Management Application Block.
|
| I trap all unhandled exceptions to one place using the following method:
|
| // --- Create an Exception Handler for Thread Exceptions ----------------
|
| Application.ThreadException += new
| ThreadExceptionEventHandler(OnThreadException);
|
| // --- Create an Exception Handler for Unhandled Exceptions -------------
|
| AppDomain currentDomain = AppDomain.CurrentDomain;
|
| currentDomain.UnhandledException += new
| UnhandledExceptionEventHandler(OnUnhandledException);
|
|
|
| private static void OnThreadException(object sender,
|
| ThreadExceptionEventArgs t)
|
| //
|
******************************************************************************
|
| // Handles Normal Thread Exceptions
|
| //
|
******************************************************************************
|
| {
|
| Exceptions.LogAndReportError(t.Exception);
|
| }
|
|
|
| private static void OnUnhandledException(object sender,
|
| UnhandledExceptionEventArgs args)
|
| //
|
******************************************************************************
|
| // Handles All Unhandled Exceptions
|
| //
|
******************************************************************************
|
| {
|
| Exceptions.LogAndReportError(args.ExceptionObject as System.Exception);
|
| }
|
| The problem I have is that exceptions that occur on a background thread
are
| not being picked up. If I do a "throw" on an exception in a background
| thread it has no place to go and just disappears as an unhandled
exception.
| How can I pick up these exceptions without handling them in the background
| thread?
|
| Thanks,
|
| Chuck Cobb
|
|
 
C

Chuck Cobb

I have an UhandledExceptionEventHandler installed in the main thread, but it
doesn't seem to be picking up exceptions that originate in another thread.
I've tried installing it in the other thread and it still doesn't seem to be
picking up the exception.
 
W

Willy Denoyette [MVP]

Don't know what could be wrong with your code, but herewith a complete
sample that may set you on the right track.

using System;
using System.Diagnostics;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
namespace Whatever
{
public class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void throwUI_Click(object sender, EventArgs e)
{
throw new Exception("Thrown from UI thread");
}
private void throwNonUI_Click(object sender, EventArgs e)
{
new Thread(delegate()
{
throw new Exception("Thrown from Aux thread");
}).Start();
}
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.throwUI = new Button();
this.throwNonUI = new Button();
this.SuspendLayout();

this.throwUI.Location = new System.Drawing.Point(12, 12);
this.throwUI.Name = "throwUI";
this.throwUI.Size = new System.Drawing.Size(95, 23);
this.throwUI.TabIndex = 0;
this.throwUI.Text = "Throw on UI";
this.throwUI.Click += new System.EventHandler(this.throwUI_Click);

this.throwNonUI.Location = new System.Drawing.Point(12, 42);
this.throwNonUI.Name = "throwUI";
this.throwNonUI.Size = new System.Drawing.Size(95, 23);
this.throwNonUI.TabIndex = 0;
this.throwNonUI.Text = "Throw on non UI";
this.throwNonUI.Click += new
System.EventHandler(this.throwNonUI_Click);

this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(283, 150);

this.Controls.Add(this.throwUI);
this.Controls.Add(this.throwNonUI);
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);
}
private Button throwUI;
private Button throwNonUI;
}
public class Program
{
[STAThread]
static void Main()
{
Application.ThreadException += delegate(object sender,
ThreadExceptionEventArgs e)
{
string errorMsg = String.Format("{0}", e.Exception.Message);
MessageBox.Show(errorMsg, "Unhandled exception",
MessageBoxButtons.OK,
MessageBoxIcon.Stop);
};
// Ignore configuration file settings - always route to
application handler.
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
AppDomain.CurrentDomain.UnhandledException += delegate(object
sender, UnhandledExceptionEventArgs e)
{
string errorMsg = String.Format("{0} - IsTerminating = {1}",
e.ExceptionObject.ToString(), e.IsTerminating);
MessageBox.Show(errorMsg, "Unhandled exception",
MessageBoxButtons.OK,
MessageBoxIcon.Stop);
};
Application.EnableVisualStyles();
Application.Run(new Form1());
}
}
}


Willy.

|I have an UhandledExceptionEventHandler installed in the main thread, but
it
| doesn't seem to be picking up exceptions that originate in another thread.
| I've tried installing it in the other thread and it still doesn't seem to
be
| picking up the exception.
|
| | > ThreadException is meant to handle unhandled exceptions on the UI
thread,
| > you need to install an UnhandledExceptionEventHandler to handle
unhandled
| > excptions on auxiliary threads.
| >
| > Willy.
| >
| > | > | I'm implementing a centralized exception handling routine using the
| > | Enterprise Library Exception Management Application Block.
| > |
| > | I trap all unhandled exceptions to one place using the following
method:
| > |
| > | // --- Create an Exception Handler for Thread
| > Exceptions ----------------
| > |
| > | Application.ThreadException += new
| > | ThreadExceptionEventHandler(OnThreadException);
| > |
| > | // --- Create an Exception Handler for Unhandled
| > Exceptions -------------
| > |
| > | AppDomain currentDomain = AppDomain.CurrentDomain;
| > |
| > | currentDomain.UnhandledException += new
| > | UnhandledExceptionEventHandler(OnUnhandledException);
| > |
| > |
| > |
| > | private static void OnThreadException(object sender,
| > |
| > | ThreadExceptionEventArgs t)
| > |
| > | //
| > |
| >
******************************************************************************
| > |
| > | // Handles Normal Thread Exceptions
| > |
| > | //
| > |
| >
******************************************************************************
| > |
| > | {
| > |
| > | Exceptions.LogAndReportError(t.Exception);
| > |
| > | }
| > |
| > |
| > |
| > | private static void OnUnhandledException(object sender,
| > |
| > | UnhandledExceptionEventArgs args)
| > |
| > | //
| > |
| >
******************************************************************************
| > |
| > | // Handles All Unhandled Exceptions
| > |
| > | //
| > |
| >
******************************************************************************
| > |
| > | {
| > |
| > | Exceptions.LogAndReportError(args.ExceptionObject as
System.Exception);
| > |
| > | }
| > |
| > | The problem I have is that exceptions that occur on a background
thread
| > are
| > | not being picked up. If I do a "throw" on an exception in a
background
| > | thread it has no place to go and just disappears as an unhandled
| > exception.
| > | How can I pick up these exceptions without handling them in the
| > background
| > | thread?
| > |
| > | Thanks,
| > |
| > | Chuck Cobb
| > |
| > |
| >
| >
|
|
 
C

Chuck Cobb

Thanks for posting this code I tried it out and the primary difference
between your code and my code is how it starts an auxiliary thread:

Your code:
new Thread(delegate()

{

ThrowExceptionInsideTryCatch();

}).Start();

My Code:

MethodInvoker mi = new MethodInvoker(ThrowExceptionOutsideTryCatch);

mi.BeginInvoke(null, null);

If I start an auxiliary thread using your method, it correctly catches the
exception from the auxiliary thread. If I start it my way, it doesn't work.
I have no idea why it should be different.



Willy Denoyette said:
Don't know what could be wrong with your code, but herewith a complete
sample that may set you on the right track.

using System;
using System.Diagnostics;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
namespace Whatever
{
public class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void throwUI_Click(object sender, EventArgs e)
{
throw new Exception("Thrown from UI thread");
}
private void throwNonUI_Click(object sender, EventArgs e)
{
new Thread(delegate()
{
throw new Exception("Thrown from Aux thread");
}).Start();
}
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.throwUI = new Button();
this.throwNonUI = new Button();
this.SuspendLayout();

this.throwUI.Location = new System.Drawing.Point(12, 12);
this.throwUI.Name = "throwUI";
this.throwUI.Size = new System.Drawing.Size(95, 23);
this.throwUI.TabIndex = 0;
this.throwUI.Text = "Throw on UI";
this.throwUI.Click += new
System.EventHandler(this.throwUI_Click);

this.throwNonUI.Location = new System.Drawing.Point(12, 42);
this.throwNonUI.Name = "throwUI";
this.throwNonUI.Size = new System.Drawing.Size(95, 23);
this.throwNonUI.TabIndex = 0;
this.throwNonUI.Text = "Throw on non UI";
this.throwNonUI.Click += new
System.EventHandler(this.throwNonUI_Click);

this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(283, 150);

this.Controls.Add(this.throwUI);
this.Controls.Add(this.throwNonUI);
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);
}
private Button throwUI;
private Button throwNonUI;
}
public class Program
{
[STAThread]
static void Main()
{
Application.ThreadException += delegate(object sender,
ThreadExceptionEventArgs e)
{
string errorMsg = String.Format("{0}", e.Exception.Message);
MessageBox.Show(errorMsg, "Unhandled exception",
MessageBoxButtons.OK,
MessageBoxIcon.Stop);
};
// Ignore configuration file settings - always route to
application handler.

Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
AppDomain.CurrentDomain.UnhandledException += delegate(object
sender, UnhandledExceptionEventArgs e)
{
string errorMsg = String.Format("{0} - IsTerminating = {1}",
e.ExceptionObject.ToString(), e.IsTerminating);
MessageBox.Show(errorMsg, "Unhandled exception",
MessageBoxButtons.OK,
MessageBoxIcon.Stop);
};
Application.EnableVisualStyles();
Application.Run(new Form1());
}
}
}


Willy.

|I have an UhandledExceptionEventHandler installed in the main thread, but
it
| doesn't seem to be picking up exceptions that originate in another
thread.
| I've tried installing it in the other thread and it still doesn't seem
to
be
| picking up the exception.
|
| | > ThreadException is meant to handle unhandled exceptions on the UI
thread,
| > you need to install an UnhandledExceptionEventHandler to handle
unhandled
| > excptions on auxiliary threads.
| >
| > Willy.
| >
| > | > | I'm implementing a centralized exception handling routine using the
| > | Enterprise Library Exception Management Application Block.
| > |
| > | I trap all unhandled exceptions to one place using the following
method:
| > |
| > | // --- Create an Exception Handler for Thread
| > Exceptions ----------------
| > |
| > | Application.ThreadException += new
| > | ThreadExceptionEventHandler(OnThreadException);
| > |
| > | // --- Create an Exception Handler for Unhandled
| > Exceptions -------------
| > |
| > | AppDomain currentDomain = AppDomain.CurrentDomain;
| > |
| > | currentDomain.UnhandledException += new
| > | UnhandledExceptionEventHandler(OnUnhandledException);
| > |
| > |
| > |
| > | private static void OnThreadException(object sender,
| > |
| > | ThreadExceptionEventArgs t)
| > |
| > | //
| > |
| >
******************************************************************************
| > |
| > | // Handles Normal Thread Exceptions
| > |
| > | //
| > |
| >
******************************************************************************
| > |
| > | {
| > |
| > | Exceptions.LogAndReportError(t.Exception);
| > |
| > | }
| > |
| > |
| > |
| > | private static void OnUnhandledException(object sender,
| > |
| > | UnhandledExceptionEventArgs args)
| > |
| > | //
| > |
| >
******************************************************************************
| > |
| > | // Handles All Unhandled Exceptions
| > |
| > | //
| > |
| >
******************************************************************************
| > |
| > | {
| > |
| > | Exceptions.LogAndReportError(args.ExceptionObject as
System.Exception);
| > |
| > | }
| > |
| > | The problem I have is that exceptions that occur on a background
thread
| > are
| > | not being picked up. If I do a "throw" on an exception in a
background
| > | thread it has no place to go and just disappears as an unhandled
| > exception.
| > | How can I pick up these exceptions without handling them in the
| > background
| > | thread?
| > |
| > | Thanks,
| > |
| > | Chuck Cobb
| > |
| > |
| >
| >
|
|
 
W

Willy Denoyette [MVP]

| Thanks for posting this code I tried it out and the primary difference
| between your code and my code is how it starts an auxiliary thread:
|
| Your code:
| new Thread(delegate()
|
| {
|
| ThrowExceptionInsideTryCatch();
|
| }).Start();
|
| My Code:
|
| MethodInvoker mi = new
MethodInvoker(ThrowExceptionOutsideTryCatch);
|
| mi.BeginInvoke(null, null);
|

This doesn't start a auxiliary thread, it delegates the
ThrowExceptionOutsideTryCatch function to execute on the 'UI thread', but as
you call this from the UI thread, it simply boils down into a simple call to
ThrowExceptionOutsideTryCatch.

Willy.
 
C

Christoph Nahr

Thanks. First, I could indeed reproduce the problem.

But as Willy already said, just calling some variant of Invoke usually
doesn't start a background thread at all -- it merely performs an
invocation on the current thread, same as just writing out the method
call. That's true even for BeginInvoke/EndInvoke (on the Control
class) which only perform cross-thread marshalling if needed.

Nevertheless, the UnhandledException handler should be triggered. But
this is apparently one of the undocumented features of the
undocumented MethodInvoker.BeginInvoke method that you were using. How
do you know it's undocumented and should not be used? Because this
method does not appear in the MSDN docs, and its IntelliSense display
does not show any description other than the prototype.

In fact, MethodInvoker offers only a single "official" invocation
method, and that's DynamicInvoke (inherited from its base class
Delegate). And when I used that documented method the
UnhandledException handler was properly triggered -- for either
ThrowException... method, whichever is executed first.

Moral: Don't use undocumented .NET Framework methods! Actually, don't
use MethodInvoker at all. It's just a simple delegate, and the three
methods it offers in addition to Delegate/MulticastDelegate are all
intended for internal Windows.Forms use only. The class should never
have been publically exposed in the first place IMO.
 
W

Willy Denoyette [MVP]

Christoph,

See inline..

Willy.

| Thanks. First, I could indeed reproduce the problem.
|
| But as Willy already said, just calling some variant of Invoke usually
| doesn't start a background thread at all -- it merely performs an
| invocation on the current thread, same as just writing out the method
| call. That's true even for BeginInvoke/EndInvoke (on the Control
| class) which only perform cross-thread marshalling if needed.
|
| Nevertheless, the UnhandledException handler should be triggered.

No it should not, the exception is thrown on the UI thead, so the
ThreadException handler is invoked.
UnhandledException handlers are only invoked when "unhandled" exceptions are
originating from non UI threads.


But
| this is apparently one of the undocumented features of the
| undocumented MethodInvoker.BeginInvoke method that you were using. How
| do you know it's undocumented and should not be used? Because this
| method does not appear in the MSDN docs, and its IntelliSense display
| does not show any description other than the prototype.
|
| In fact, MethodInvoker offers only a single "official" invocation
| method, and that's DynamicInvoke (inherited from its base class
| Delegate). And when I used that documented method the
| UnhandledException handler was properly triggered -- for either
| ThrowException... method, whichever is executed first.
|
| Moral: Don't use undocumented .NET Framework methods! Actually, don't
| use MethodInvoker at all. It's just a simple delegate, and the three
| methods it offers in addition to Delegate/MulticastDelegate are all
| intended for internal Windows.Forms use only. The class should never
| have been publically exposed in the first place IMO.
| --
| http://www.kynosarges.de
 
C

Christoph Nahr

No it should not, the exception is thrown on the UI thead, so the
ThreadException handler is invoked.
UnhandledException handlers are only invoked when "unhandled" exceptions are
originating from non UI threads.

Willy, haven't you read the rest of my post? The UnhandledException
handler *was* triggered when BeginInvoke was changed to DynamicInvoke,
or when the exception methods were called directly. The
ThreadException handler *was not* triggered.

As to why, I'm not sure. In the test program the exception is thrown
before a form is even constructed, so technically there is no GUI
(Windows Forms) thread at the time of the exception. Remember that
ThreadException is a Windows Forms feature, not a general threading
feature. Possibly it requires a window event queue to work.
 
C

Christoph Nahr

Oh dear, oh dear. Attention: I'm a dumbass. Willy is also a dumbass.
Chuck is very smart and was actually mostly right. Should have read
the chapter on asynchronous operations in Jeff Richter's "CLR via C#"
before replying. I'm very sorry... here comes the correct version.

I got completely sidetracked by reminiscences of Control.BeginInvoke
which does not start a new thread. But in fact, the MethodInvoker
methods Invoke/BeginInvoke/EndInvoke are undocumented because they're
*auto-generated* for each delegate class. The BeginInvoke method is
proper to call and does, in fact, start a new background thread.

I no longer understand why the MSDN docs say that MethodInvoker.Invoke
is "for internal use" only; that does not seem to make sense. All of
these methods should be proper for anyone's use.

Okay. So why didn't UnhandledException get called? That's because
the auto-generated BeginInvoke method of a delegate executes on a
thread in the CLR thread pool, and any exceptions thrown by a thread
pool thread are automatically swallowed by the CLR until the
corresponding EndInvoke is called, at which point they're thrown.

So Chuck should see his UnhandledException handler activated when the
BeginInvoke call is paired with an EndInvoke call.

All this is explained in Jeff Richter's "CLR via C#", chapter 23,
pages 614f. Ironically, it's the book I tell everyone to read... that
was the last chapter I had not gotten around to read until now.
 
C

Christoph Nahr

I no longer understand why the MSDN docs say that MethodInvoker.Invoke
is "for internal use" only; that does not seem to make sense. All of
these methods should be proper for anyone's use.

Jesus, what's wrong with me? The MSDN docs don't say that at all,
that was some other method I was looking up. Scratch that paragraph.
 
W

Willy Denoyette [MVP]

Well, actually I didn't pay attention to the BeginIvoke call (I even didn't
look at the attached code), I only noticed the MethodInvoker, which was
enough to conclude that the exception was not thrown on an "auxiliary"
thread (but being a dumbass, I wrongly assumed that it was on the UI
thread). Notice that I call an "auxiliary thread", a thread that is
explicitly created via a call to a Thread class constructor. Only unhandled
exceptions originating from "auxiliary" threads are routed to the
UnhandledException handler, 'unhandled' exceptions originating from pool
threads are not, they have to be handled explicitly, as you correctly
mentioned. Note that every BeginInvoke on a delegate has to be paired with
an EndInvoke call, this is mandatory on the CLR, failing to do this may
result in handle leaks.

Willy.



| Oh dear, oh dear. Attention: I'm a dumbass. Willy is also a dumbass.
| Chuck is very smart and was actually mostly right. Should have read
| the chapter on asynchronous operations in Jeff Richter's "CLR via C#"
| before replying. I'm very sorry... here comes the correct version.
|
| I got completely sidetracked by reminiscences of Control.BeginInvoke
| which does not start a new thread. But in fact, the MethodInvoker
| methods Invoke/BeginInvoke/EndInvoke are undocumented because they're
| *auto-generated* for each delegate class. The BeginInvoke method is
| proper to call and does, in fact, start a new background thread.
|
| I no longer understand why the MSDN docs say that MethodInvoker.Invoke
| is "for internal use" only; that does not seem to make sense. All of
| these methods should be proper for anyone's use.
|
| Okay. So why didn't UnhandledException get called? That's because
| the auto-generated BeginInvoke method of a delegate executes on a
| thread in the CLR thread pool, and any exceptions thrown by a thread
| pool thread are automatically swallowed by the CLR until the
| corresponding EndInvoke is called, at which point they're thrown.
|
| So Chuck should see his UnhandledException handler activated when the
| BeginInvoke call is paired with an EndInvoke call.
|
| All this is explained in Jeff Richter's "CLR via C#", chapter 23,
| pages 614f. Ironically, it's the book I tell everyone to read... that
| was the last chapter I had not gotten around to read until now.
| --
| http://www.kynosarges.de
 

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