Suppose I have a function that throws an exception. Suppose this function is called by a third-party DLL, and the third-party DLL will handle the exception I’ve thrown.
If Visual Studio decides that the third-party DLL is not "user code" (as seen in the image below) then it will stop on my exception by default, even though it gets handled later. It isn’t exactly wrong to do so; it clearly explains that the exception was unhandled by user code. But what is it that makes Visual Studio call some DLLs "user code" and others not?
I had a theory that this happens because the symbols aren’t loaded, but there are modules in the list that have loaded symbols but are still not considered "user code".
Yes, without a .pdb file the debugger can't tell whether it is user code or not. It is explained reasonably well in the MSDN article:
To distinguish user code from non-user code, Just My Code looks at three things: DBG Files, PDB files, and optimization.
In a standard Debug build, optimization is turned off and debug symbols are created for all modules. When you run a debug build, those modules are considered to be user code. If I call a library function that is optimized and does not have debug symbols, however, it is not user code. Just My Code prevents execution from stopping at breakpoints in the library code, which is usually not code you are interested in debugging. In the Breakpoints window, those breakpoints will appear with the Disabled Breakpoint icon.