Why is .NET exception not caught by try/catch block?
I believe I understand the problem. The exception is being caught, the issue is confusion over the debugger's behavior and differences in the debugger settings among each person trying to repro it.
In the 3rd case from your repro I believe you are getting the following message: "NoViableAltException was unhandled by user code" and a callstack that looks like this:
[External Code] > TestAntlr-3.1.exe!TimeDefLexer.mTokens() Line 852 + 0xe bytes C# [External Code] TestAntlr-3.1.exe!TimeDefParser.prog() Line 141 + 0x14 bytes C# TestAntlr-3.1.exe!TestAntlr_3._1.Program.ParseTest(string Text = "foobar;") Line 49 + 0x9 bytes C# TestAntlr-3.1.exe!TestAntlr_3._1.Program.Main(string[] args = {string[0x00000000]}) Line 30 + 0xb bytes C# [External Code]
If you right click in the callstack window and run turn on show external code you see this:
Antlr3.Runtime.dll!Antlr.Runtime.DFA.NoViableAlt(int s = 0x00000000, Antlr.Runtime.IIntStream input = {Antlr.Runtime.ANTLRStringStream}) + 0x80 bytes Antlr3.Runtime.dll!Antlr.Runtime.DFA.Predict(Antlr.Runtime.IIntStream input = {Antlr.Runtime.ANTLRStringStream}) + 0x21e bytes > TestAntlr-3.1.exe!TimeDefLexer.mTokens() Line 852 + 0xe bytes C# Antlr3.Runtime.dll!Antlr.Runtime.Lexer.NextToken() + 0xc4 bytes Antlr3.Runtime.dll!Antlr.Runtime.CommonTokenStream.FillBuffer() + 0x147 bytes Antlr3.Runtime.dll!Antlr.Runtime.CommonTokenStream.LT(int k = 0x00000001) + 0x2d bytes TestAntlr-3.1.exe!TimeDefParser.prog() Line 141 + 0x14 bytes C# TestAntlr-3.1.exe!TestAntlr_3._1.Program.ParseTest(string Text = "foobar;") Line 49 + 0x9 bytes C# TestAntlr-3.1.exe!TestAntlr_3._1.Program.Main(string[] args = {string[0x00000000]}) Line 30 + 0xb bytes C# [Native to Managed Transition] [Managed to Native Transition] mscorlib.dll!System.AppDomain.ExecuteAssembly(string assemblyFile, System.Security.Policy.Evidence assemblySecurity, string[] args) + 0x39 bytes Microsoft.VisualStudio.HostingProcess.Utilities.dll!Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() + 0x2b bytes mscorlib.dll!System.Threading.ThreadHelper.ThreadStart_Context(object state) + 0x3b bytes mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state) + 0x81 bytes mscorlib.dll!System.Threading.ThreadHelper.ThreadStart() + 0x40 bytes
The debugger's message is telling you that an exception originating outside your code (from NoViableAlt) is going through code you own in TestAntlr-3.1.exe!TimeDefLexer.mTokens() without being handled.
The wording is confusing, but it does not mean the exception is uncaught. The debugger is letting you know that code you own mTokens()" needs to be robust against this exception being thrown through it.
Things to play with to see how this looks for those who didn't repro the problem:
- Go to Tools/Options/Debugging and turn off "Enable Just My code (Managed only)". or option.
- Go to Debugger/Exceptions and turn off "User-unhandled" for Common-Language Runtime Exceptions.
Regardless of whether the assembly has been compiled as a release build the exception should certainly 'bubble' up to the caller, there's no reason an assembly not being compiled in debug mode should have any affect on that.
I'd agree with Daniel is suggesting that perhaps the exception is occurring on a separate thread - try hooking the thread exception event in Application.ThreadException. This should be raised when any unhandled thread exception occurs. You could adapt your code thus:-
using System.Threading;
...
void Application_ThreadException(object sender, ThreadExceptionEventArgs e) {
throw new ParserException(e.Exception.Message, e.Exception);
}
...
var exceptionHandler =
new ThreadExceptionEventHandler(Application_ThreadException);
Application.ThreadException += exceptionHandler;
try {
// Execution stopped at parser.prog()
TimeDefParser.prog_return prog_ret = parser.prog();
return prog_ret == null ? null : prog_ret.value;
}
catch (Exception ex) {
throw new ParserException(ex.Message, ex);
}
finally {
Application.ThreadException -= exceptionHandler;
}
I can tell you what's happening here...
Visual Studio is breaking because it thinks the exception is unhandled. What does unhandled mean? Well, in Visual Studio, there is a setting in the Tools... Options... Debugging... General... "Enable Just My Code (Managed only)". If this is checked and if the exception propagates out of your code and out to a stack frame associated with a method call that exists in an assembly which is "NOT YOUR CODE" (for example, Antlr), that is considered "unhandled". I turn off that Enable Just My Code feature for this reason. But, if you ask me, this is lame... let's say you do this:
ExternalClassNotMyCode c = new ExternalClassNotMyCode();
try {
c.doSomething( () => { throw new Exception(); } );
}
catch ( Exception ex ) {}
doSomething calls your anonymous function there and that function throws an exception...
Note that this is an "unhandled exception" according to Visual Studio if "Enable Just My Code" is on. Also, note that it stops as if it were a breakpoint when in debug mode, but in a non-debugging or production environment, the code is perfectly valid and works as expected. Also, if you just "continue" in the debugger, the app goes on it's merry way (it doesn't stop the thread). It is considered "unhandled" because the exception propagates through a stack frame that is NOT in your code (i.e. in the external library). If you ask me, this is lousy. Please change this default behavior Microsoft. This is a perfectly valid case of using Exceptions to control program logic. Sometimes, you can't change the third party library to behave any other way, and this is a very useful way to accomplish many tasks.
Take MyBatis for example, you can use this technique to stop processing records that are being collected by a call to SqlMapper.QueryWithRowDelegate.
Are you using .Net 1.0 or 1.1? If so then catch(Exception ex) won't catch exceptions from unmanaged code. You'll need to use catch {} instead. See this article for further details:
http://www.netfxharmonics.com/2005/10/net-20-trycatch-and-trycatchexception/