Home All Groups Group Topic Archive Search About

InnerException > BackgroundWorker > MyApplication.UnhandledExcepti

Author
9 Nov 2006 10:19 AM
Dick
I'm trying to understand why the MyApplication.UnhandledException event
handler behaves differently when the exception originates from the background
thread of a BackgroundWorker component.

e.g.

The event handler displays the message from the exception and from the inner
exception:

Partial Friend Class MyApplication
  Private Sub MyApplication_UnhandledException(ByVal sender As Object, ByVal
e As Microsoft.VisualBasic.ApplicationServices.UnhandledExceptionEventArgs)
Handles Me.UnhandledException
    If e.Exception.InnerException Is Nothing Then
      MsgBox(e.Exception.Message & " Nothing")
    Else
      MsgBox(e.Exception.Message & " " & e.Exception.InnerException.Message)
    End If
    e.ExitApplication = False
  End Sub
End Class

The following code behaves as expected, the event handler displaying “Blah
Foo”.

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles Button1.Click
  Try
    Throw New Exception("Foo")
  Catch ex As Exception
    Throw New Exception("Blah", ex)
  End Try
End Sub

But the following code behaves unexpectedly, the event handler displaying
“Foo Nothing”, which, I guess, comes as a result of the base exception being
passed to the event handler.

Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles Button2.Click
  Me.BackgroundWorker1.RunWorkerAsync()
End Sub

Private Sub BackgroundWorker1_DoWork(ByVal sender As Object, ByVal e As
System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
  Try
    Throw New Exception("Foo")
  Catch ex As Exception
    Throw New Exception("Blah", ex)
  End Try
End Sub

Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As Object,
ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles
BackgroundWorker1.RunWorkerCompleted
  Throw e.Error
End Sub

I’d appreciate an explanation as to why and also an indication as to how I
can get the complete exception tree.

Author
10 Nov 2006 3:30 AM
Jeffrey Tan[MSFT]
Hi Dick,

Yes, I can reproduce the problem with the code snippet you provided. Below
is my detailed analysis process for this problem, for your information:

VB2005 MyApplication.UnhandledException internally leverages
Application.ThreadException to implement the magic, I have proved this in
the link below:
http://groups.google.com/group/microsoft.public.dotnet.framework.windowsform
s/msg/5422486e68eddae7?hl=zh-CN&

So I tried to disable "Application Framework" in my testing VB2005 project
and used Application.ThreadException directly to handle this exceptoin,
like this:
Public Class Form1
    <STAThread()> _
    Shared Sub Main()
        AddHandler Application.ThreadException, AddressOf OnThreadException
        Application.Run(New Form1())
    End Sub

    Public Shared Sub OnThreadException(ByVal sender As Object, ByVal t As
ThreadExceptionEventArgs)
        If t.Exception.InnerException Is Nothing Then
            MsgBox(t.Exception.Message & " Nothing")
        Else
            MsgBox(t.Exception.Message & " " &
t.Exception.InnerException.Message)
        End If
    End Sub
....
End Class

Oh, I find the problem still exist, Application.ThreadException will also
only report the inner-most exception "Foo".

However, if we catch the exception in BackgroundWorker1_RunWorkerCompleted
with code snippet below, we will get the 2 exceptions without any problem:
Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As
System.Object, ByVal e As
System.ComponentModel.RunWorkerCompletedEventArgs) Handles
BackgroundWorker1.RunWorkerCompleted
        Try
               Throw e.Error
        Catch ex As Exception
            If ex.InnerException Is Nothing Then
                MsgBox(ex.Message & " Nothing")
            Else
                MsgBox(ex.Message & " " & ex.InnerException.Message)
            End If
        End Try
End Sub

This reveals that there is some magic happen during the unwinding of the
stack for exception.

By comparing the call stack of BackgroundWorker1_RunWorkerCompleted()
method and OnThreadException() method, I find that the exception handling
unwinding stops at "System.Windows.Forms.Control.InvokeMarshaledCallbacks"
internal method.

Then I launched Reflector to examine
"System.Windows.Forms.Control.InvokeMarshaledCallbacks" source code. My
eyes stop on the following code snippet:

Private Sub InvokeMarshaledCallbacks()
            .....
             Try
                  If (NativeWindow.WndProcShouldBeDebuggable AndAlso Not
entry1.synchronous) Then
                        Me.InvokeMarshaledCallback(entry1)
                  Else
                        Try
                              Me.InvokeMarshaledCallback(entry1)
                        Catch exception1 As  Exception
                              entry1.exception = exception1.GetBaseException
                        End Try
                  End If
            Finally
                  entry1.Complete
                  If ((Not NativeWindow.WndProcShouldBeDebuggable AndAlso
(Not entry1.exception Is Nothing)) AndAlso Not entry1.synchronous) Then
                        Application.OnThreadException(entry1.exception)
                  End If
            End Try
          .....
End Sub

Yes, InvokeMarshaledCallbacks() method catches the exception we throw and
then calls "exception1.GetBaseException" method to get the inner-most
exception object and pass it to the Application.OnThreadException() method.
We finally find the root cause now!

Then I want to understand why Winform InvokeMarshaledCallbacks() method
will translate the exception into inner-most exception(Foo) for
Application.OnThreadException() method. I performed some search in our
internal database with one existing record regarding our problem.

Based on the winform comfirmation in the record, our analysis is correct of
the root cause and this behavior is intended. The reason was to prevent the
user from seeing too much of the Windows.Forms internal mechanisms. This is
because the winform's default error dialog also leverages
Application.ThreadException to show the exception details. .Net Winform
team trims the other exceptions information so that the default error
dialog will not display all the details to the end user.

Also, some MSFTs have sugguested to change this behavior. However, .Net
Winform team thinks that changing the exception to throw is a breaking
change and for this reason WinForms will keep sending the innermost
exception to the Application.ThreadException handler.

Hope this helps.

Best regards,
Jeffrey Tan
Microsoft Online Community Support
==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.

AddThis Social Bookmark Button