We recently had some questions on our internal Visual Studio Debugger forum about the exceptions dialog. So I decided to take a stab at explaining it.
Just My Code
The first step to understanding this dialog is to understand a feature called ‘Just My Code’ (enabled/disabled with ‘Tools->Options->Debugging->Enable Just My Code’). Just My Code is currently a managed-only debugging feature. When enabled, the debugger divides up all the code running in the debugged process into two groups – your code, and non-user code (system code). It then tries to hide the details of system code from you so that you can concentrate on looking at your own code.
Next concept to explain: ‘User-Unhandled’ exceptions. An exception becomes ‘User-Unhandled’ after:
- An exception travels through your code (your code either throws the exception or calls into system code which throws an exception back to you)
- Your code doesn’t catch the exception
- The exception is caught by system code
In other words: ‘User-unhandled’ does not necessarily mean that your app is actually crashing. The debugger is trying to tell you about an exception which is probably interesting before this exception is swallowed up by the system. After catching the exception, the system might fall over and die, or it might be totally expected (in which case you should go to Debug->Exceptions and turn off stopping on ‘User Unhandled’ exceptions for your app).
Example: You are building a WinForms app which has a bug in its handler for some button which will cause it to deref null. If you run this app outside of a debugger, and then attach the debugger, WinForms will quietly swallow the exception. But if you have ‘User unhandled’ exceptions enabled, the debugger will stop and you can easily find out what went wrong.
Now to the dialog
Now that we have the concepts, let’s explain how they are expressed in the dialog.
For exceptions that don’t support Just My Code (C++ Exceptions, Managed Debug Assistants, Win32 Exceptions, Common Language Runtime Exceptions if Just My Code is disabled) – the debugger will show the ‘User-Unhandled’ checkbox as checked and disabled. What this is trying to tell you is that this setting is ‘N/A’. For this type of code, the debugger doesn’t divide the world up into user code and non-user code. So an exception could never go user-unhandled.
For these exceptions, the ‘Thrown’ column has the same meaning that it does in other debuggers (WinDbg calls this ‘first chance’, VS 2002/3 referred to this as ‘Break’ instead of ‘Continue’).
For exceptions Common Language Runtime Exceptions (with Just My Code enabled): Both the ‘Thrown’ and ‘User-Unhandled’ column can be checked. ‘User-unhandled’ has the meaning discussed above. Checking ‘Thrown’ will cause the debugger to stop when either user code throws the exception, or when system code throws the exception back to user code. In other words, the debugger will not stop for internal exceptions within the system code.
What about unhandled exceptions? The debugger always stops on unhandled exceptions since the process will otherwise exit. Since the debugger always stops, there doesn’t need to be a checkbox for this one.