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 callsMyForm_Load()
event handler and it callsMyComboBox_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 toFillMyDataTable()
located betweenTry
/Catch
.I'm always on main thread (
ManagedThreadId
is stored duringStartup
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)
.
- Topmost frame is
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:
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
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.