2
votes

I'm getting different behavior when running the same code inside and outside the Visual Studio.

Private Sub MyApplication_Startup(...) Handles Me.Startup
    '--- handler for unhandled exceptions
    AddHandler AppDomain.CurrentDomain.UnhandledException, AddressOf UnhandledExHandler
    '--- handler 0
    AddHandler System.Windows.Forms.Application.ThreadException, AddressOf ThreadExHandler
    Utils.RememberMainThreadId()
End Sub

Sub OpenMyForm()  'entry point
    Debug.Assert(Utils.RunningOnMainThread())
    Try
        MyForm.Show()  
    Catch ex As Exception  '--- handler 1
        LogError(ex)   '--- goes here only if launched outside the Visual Studio
    End Try
End Sub

Sub MyForm_Load() Handles MyBase.Load
    Debug.Assert(Utils.RunningOnMainThread())
    FillMyDataTable() '---if I put try/catch here, it will always work (tested)
End Sub

Sub FillMyDataTable()
    Try
        New SqlClient.SqlDataAdapter(sqlCmd).Fill(myDataTable)
    Catch ex As Exception '--- handler 2
        If ex.Number = Constants.ConnectionBroken then
            ReconnectRetry()
        Else
            Throw  '--- enters UnhandledExHandler() when in Visual Studio
        End If
    End Try
End Sub

In Visual Studio, error in bad SQL command goes to UnhandledExHandler(), but if the same EXE is launched outside the VS, Catch ex As Exception inside Wrapper() is triggered (this is expected result). What is wrong here?

  • Visual Studio error message is An unhandled exception of type 'System.Data.SqlClient.SqlException' occurred in filename.exe.

  • This is happening during call to MyForm.Show() which indirectly calls MyForm_Load() event handler and it calls MyComboBox_SelectedIndexChanged() event handler (and this one indirectly executes the query).

  • If I find and click Wrapper() stack frame during break on exception, I can see the higlighted call to FillMyDataTable() located between Try/Catch.

  • I'm always on main thread (ManagedThreadId is stored during Startup event, then multiple asserts verifying it are inserted in the above code).

  • Stack traces are exactly the same (checked via diff) except the bottommost frame (System.Windows.Forms.NativeWindow.DebuggableCallback() vs. System.Windows.Forms.NativeWindow.Callback()).

    • Topmost frame is System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection).
  • This was not the problem until yesterday. I think I have changed some option.

  • Restart of Visual Studio did not help.

  • There is no change in behavior if Exception assistant in enabled or disabled.

  • Options and exception settings are like this:
    enter image description here
    enter image description here

  • Stack trace is:

myApp.exe!myApp.frmAPP_PrenosWizard.frmFT_PrenosWizard_Load(Object sender, System.EventArgs e) Line 125 Basic
System.Windows.Forms.dll!System.Windows.Forms.Form.OnLoad(System.EventArgs e) + 0x1d5 bytes 
System.Windows.Forms.dll!System.Windows.Forms.Form.OnCreateControl() + 0x55 bytes   
System.Windows.Forms.dll!System.Windows.Forms.Control.CreateControl(bool fIgnoreVisible) + 0x181 bytes  
System.Windows.Forms.dll!System.Windows.Forms.Control.CreateControl() + 0x24 bytes  
System.Windows.Forms.dll!System.Windows.Forms.Control.WmShowWindow(ref System.Windows.Forms.Message m) + 0x98 bytes 
System.Windows.Forms.dll!System.Windows.Forms.Control.WndProc(ref System.Windows.Forms.Message m) + 0x2b6 bytes 
System.Windows.Forms.dll!System.Windows.Forms.ScrollableControl.WndProc(ref System.Windows.Forms.Message m) + 0x2a bytes    
System.Windows.Forms.dll!System.Windows.Forms.ContainerControl.WndProc(ref System.Windows.Forms.Message m) + 0x10 bytes 
System.Windows.Forms.dll!System.Windows.Forms.Form.WmShowWindow(ref System.Windows.Forms.Message m) + 0x41 bytes    
System.Windows.Forms.dll!System.Windows.Forms.Form.WndProc(ref System.Windows.Forms.Message m) + 0x154 bytes    
System.Windows.Forms.dll!System.Windows.Forms.Control.ControlNativeWindow.OnMessage(ref System.Windows.Forms.Message m) + 0x10 bytes    
System.Windows.Forms.dll!System.Windows.Forms.Control.ControlNativeWindow.WndProc(ref System.Windows.Forms.Message m) + 0x31 bytes  
System.Windows.Forms.dll!System.Windows.Forms.NativeWindow.DebuggableCallback(System.IntPtr hWnd, int msg, System.IntPtr wparam, System.IntPtr lparam) + 0x57 bytes 
[Native to Managed Transition]  
[Managed to Native Transition]  
System.Windows.Forms.dll!System.Windows.Forms.UnsafeNativeMethods.CreateWindowEx(int dwExStyle, string lpszClassName, string lpszWindowName, int style, int x, int y, int width, int height, System.Runtime.InteropServices.HandleRef hWndParent, System.Runtime.InteropServices.HandleRef hMenu, System.Runtime.InteropServices.HandleRef hInst, object pvParam) + 0x3c bytes  
System.Windows.Forms.dll!System.Windows.Forms.NativeWindow.CreateHandle(System.Windows.Forms.CreateParams cp) + 0x225 bytes 
System.Windows.Forms.dll!System.Windows.Forms.Control.CreateHandle() + 0x125 bytes  
System.Windows.Forms.dll!System.Windows.Forms.Form.CreateHandle() + 0x9f bytes  
System.Windows.Forms.dll!System.Windows.Forms.Control.Handle.get() + 0x45 bytes 
System.Windows.Forms.dll!System.Windows.Forms.Form.SetVisibleCore(bool value) + 0x160 bytes 
System.Windows.Forms.dll!System.Windows.Forms.Control.Show() + 0x10 bytes   
myApp.exe!myApp.APP.clsPrenos.ZobrazWizard(myApp.frmAPP_PrenosWizard.enRezimPrenosu rezim, myApp.APP.clsEntita.enEntita zdrojEntita, Integer zdrojID, myApp.APP.clsEntita.enEntita cielEntita, Integer cielID, System.Data.SqlClient.SqlConnection cn1) Line 8212 + 0xa bytes   Basic
myApp.exe!myApp.frmMain.FCreateTransferUI(myApp.frmAPP_PrenosWizard.enRezimPrenosu mode, myApp.APP.clsEntita.enEntita sourceEntity, Integer sourceID, myApp.APP.clsEntita.enEntita targetEntity, Integer targetID, System.Data.SqlClient.SqlConnection cn1) Line 2410 + 0x17 bytes  Basic
myApp.exe!myApp.frmMain.frmMain_Receive(Object sender, myApp.clsFisCommandProcessor.ReceivedEventArgs e) Line 239 + 0xa7 bytes  Basic
myApp.exe!myApp.clsFisCommandProcessor.raise_Received(Object sender, myApp.clsFisCommandProcessor.ReceivedEventArgs e) Line 96 + 0x2e bytes Basic
myApp.exe!myApp.clsFisCommandProcessor.Execute(myApp.clsFisCommand command, Object sender) Line 136 + 0x57 bytes    Basic
myApp.exe!myApp.clsFisCommandProcessor.Execute(myApp.clsFisCommand() commands, Object sender) Line 128 + 0x27 bytes Basic
myApp.exe!myApp.clsFisCommandProcessor.Execute(String scan, Boolean requireMarking, Object sender) Line 122 + 0x30 bytes    Basic
myApp.exe!myApp.frmCommandPad.TSMI_Execute_Click(Object sender, System.EventArgs e) Line 94 + 0x51 bytes    Basic
System.Windows.Forms.dll!System.Windows.Forms.ToolStripItem.RaiseEvent(object key, System.EventArgs e) + 0x58 bytes 
System.Windows.Forms.dll!System.Windows.Forms.ToolStripMenuItem.OnClick(System.EventArgs e) + 0x46 bytes    
System.Windows.Forms.dll!System.Windows.Forms.ToolStripItem.HandleClick(System.EventArgs e) + 0x6e bytes    
System.Windows.Forms.dll!System.Windows.Forms.ToolStripItem.FireEventInteractive(System.EventArgs e, System.Windows.Forms.ToolStripItemEventType met) + 0x83 bytes  
System.Windows.Forms.dll!System.Windows.Forms.ToolStripItem.FireEvent(System.EventArgs e, System.Windows.Forms.ToolStripItemEventType met) + 0x118 bytes    
System.Windows.Forms.dll!System.Windows.Forms.ToolStripMenuItem.ProcessCmdKey(ref System.Windows.Forms.Message m, System.Windows.Forms.Keys keyData) + 0x47 bytes   
System.Windows.Forms.dll!System.Windows.Forms.ToolStripManager.ProcessShortcut(ref System.Windows.Forms.Message m, System.Windows.Forms.Keys shortcut) + 0x2dc bytes    
System.Windows.Forms.dll!System.Windows.Forms.ToolStripManager.ProcessCmdKey(ref System.Windows.Forms.Message m, System.Windows.Forms.Keys keyData) + 0x2d bytes    
System.Windows.Forms.dll!System.Windows.Forms.ContainerControl.ProcessCmdKey(ref System.Windows.Forms.Message msg, System.Windows.Forms.Keys keyData) + 0x3c bytes  
System.Windows.Forms.dll!System.Windows.Forms.Form.ProcessCmdKey(ref System.Windows.Forms.Message msg, System.Windows.Forms.Keys keyData) + 0x29 bytes  
System.Windows.Forms.dll!System.Windows.Forms.Control.ProcessCmdKey(ref System.Windows.Forms.Message msg, System.Windows.Forms.Keys keyData) + 0x96 bytes   
System.Windows.Forms.dll!System.Windows.Forms.TextBoxBase.ProcessCmdKey(ref System.Windows.Forms.Message msg, System.Windows.Forms.Keys keyData) + 0xda bytes   
System.Windows.Forms.dll!System.Windows.Forms.Control.PreProcessMessage(ref System.Windows.Forms.Message msg) + 0x90 bytes  
System.Windows.Forms.dll!System.Windows.Forms.Control.PreProcessControlMessageInternal(System.Windows.Forms.Control target, ref System.Windows.Forms.Message msg) + 0x101 bytes 
System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.PreTranslateMessage(ref System.Windows.Forms.NativeMethods.MSG msg) + 0xf6 bytes    
System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.System.Windows.Forms.UnsafeNativeMethods.IMsoComponent.FPreTranslateMessage(ref System.Windows.Forms.NativeMethods.MSG msg) + 0x5 bytes 
System.Windows.Forms.dll!System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(int dwComponentID, int reason, int pvLoopData) + 0x22e bytes  
System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(int reason, System.Windows.Forms.ApplicationContext context) + 0x177 bytes  
System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.RunMessageLoop(int reason, System.Windows.Forms.ApplicationContext context) + 0x61 bytes    
System.Windows.Forms.dll!System.Windows.Forms.Application.Run(System.Windows.Forms.ApplicationContext context) + 0x18 bytes 
Microsoft.VisualBasic.dll!Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.OnRun() + 0x81 bytes    
Microsoft.VisualBasic.dll!Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.DoApplicationModel() + 0xef bytes   
Microsoft.VisualBasic.dll!Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.Run(string[] commandLine) + 0x68 bytes  
[Native to Managed Transition]  
[Managed to Native Transition]  
mscorlib.dll!System.AppDomain.nExecuteAssembly(System.Reflection.Assembly assembly, string[] args) + 0x19 bytes 
mscorlib.dll!System.Runtime.Hosting.ManifestRunner.Run(bool checkAptModel) + 0x6e bytes 
mscorlib.dll!System.Runtime.Hosting.ManifestRunner.ExecuteAsAssembly() + 0x84 bytes 
mscorlib.dll!System.Runtime.Hosting.ApplicationActivator.CreateInstance(System.ActivationContext activationContext, string[] activationCustomData) + 0x65 bytes 
mscorlib.dll!System.Runtime.Hosting.ApplicationActivator.CreateInstance(System.ActivationContext activationContext) + 0xa bytes 
mscorlib.dll!System.Activator.CreateInstance(System.ActivationContext activationContext) + 0x3e bytes   
Microsoft.VisualStudio.HostingProcess.Utilities.dll!Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssemblyDebugInZone() + 0x23 bytes   
mscorlib.dll!System.Threading.ThreadHelper.ThreadStart_Context(object state) + 0x66 bytes   
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state) + 0x6f bytes    
mscorlib.dll!System.Threading.ThreadHelper.ThreadStart() + 0x44 bytes   
1
what is the exception stack trace? E.g. if LogError throws an exception when the debugger is attached vs. not you will see an exception with a stack trace inside LogErrorta.speot.is
This is expected (Feature) IF you had the Thrown checkbox for CLR Exception checked for adequate exception. Are you sure you have the settings posted?S22
This is entirely intentional, the Application.ThreadException event is intentionally disabled when you use a debugger. Pretty essential to let you diagnose and repair exceptions easily. You should only be registering these event handlers when Debugger.IsAttached is False.Hans Passant
@HansPassant – if you mentioned Application.ThreadException event, I understand you were commenting on Handler 0 (see updated code). Thank you for the comment. But in this case Handler 1 is not catching anything – that's the problem. The exception cannot poperly bubble up through Form.Show().miroxlav
If can only ever catch exceptions that occur in the constructor. Which is about never. If your Load event fires when the constructor hasn't finished yet then you have a problem you need to fix. Set a breakpoint on the Load event and look at the call stack. Best thing to do is not use the Load event, there is no point whatsoever putting that code there. It belongs in the constructor. You will also avoid this nasty randomizer.Hans Passant

1 Answers

4
votes

This question has many moving parts, not sure I can do justice to all of them. Starting point is that you should not subscribe those events if you use a debugger. Wrap that code with If Not System.Diagnostics.Debugger.IsAttached Then so you can properly diagnose and repair unhandled exceptions.

A significant randomizer is what will happen with exceptions that are thrown in a Load event handler. Microsoft had a great deal of trouble properly handling exceptions that are raised while a Windows callback is active. Such as the one that causes the Load event to fire. They changed the rules at Vista, again at Win7, again at Win8. They are different if you run the 32-bit of the 64-bit version. And whether your program runs as a 32-bit or 64-bit process. Using a debugger alters the behavior. The Program Compatibility Assistant may appear when the exception occurs, asking you if you want your program to be "compatible". Everybody says Yes of course, a very bad idea. Adding them up, you can get one of thirty-two different outcomes. Ouch.

It is formally described in this MSDN article but nobody understands it. Not until it doesn't do what they hope for, covered in a question like this one. Mitigating circumstance is that this only bytes when you debug an app, the normal exception back-stop in the message loop ensures this doesn't get out of hand on your user's machine. In other words, what you see when you press Ctrl+F5 or run the program with Explorer is the way you can expect it to work.

This is a problem that affects any GUI program on Windows. But it has a knack of being especially troublesome in a Winforms app, programmers use the Load event entirely too much. Deluded into it by it being the default event for the Form class, it takes but a double-click to generate it. Especially so in VB.NET, it doesn't automatically create the constructor for a form and Visual Basic has a legacy of using the Load event to initialize a form, goes back to VB6.

I normally avoid judging programming practices I see, but using the Load event is today a really rather bad practice because of these problems. Always favor the constructor to initialize a class, a rule that any .NET programmer knows, it isn't different for the Form class. There are exceedingly few reasons to have to use Load, covered in this post.