为什么 .NET 异常没有被 try/catch 块捕获? [英] Why is .NET exception not caught by try/catch block?

查看:32
本文介绍了为什么 .NET 异常没有被 try/catch 块捕获?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用用于 C# 的 ANTLR 解析器库处理一个项目.我已经建立了一个语法来解析一些文本,它运行良好.但是,当解析器遇到非法或意外的标记时,它会抛出许多异常之一.问题在于,在某些情况下(并非全部),我的 try/catch 块不会捕获它,而是作为未处理的异常停止执行.

对我来说,问题是我无法在其他任何地方复制这个问题,但在我的完整代码中.调用堆栈显示异常肯定发生在我的 try/catch(Exception) 块中.我唯一能想到的是在我的代码和抛出异常的代码之间发生了一些 ANTLR 程序集调用,并且该库没有启用调试,所以我无法单步执行.我想知道不可调试的程序集是否禁止异常冒泡?调用堆栈如下所示;外部程序集调用在 Antlr.Runtime 中:

<前>Expl.Itinerary.dll!TimeDefLexer.mTokens() 第 1213 行 C#Antlr3.Runtime.dll!Antlr.Runtime.Lexer.NextToken() + 0xfc 字节Antlr3.Runtime.dll!Antlr.Runtime.CommonTokenStream.FillBuffer() + 0x22c 字节Antlr3.Runtime.dll!Antlr.Runtime.CommonTokenStream.LT(int k = 1) + 0x68 字节Expl.Itinerary.dll!TimeDefParser.prog() Line 109 + 0x17 bytes C#Expl.Itinerary.dll!Expl.Itinerary.TDLParser.Parse(string Text = "", Expl.Itinerary.IItinerary Itinerary = {Expl.Itinerary.MemoryItinerary}) 第 17 行 + 0xa 字节 C#

Parse() 中最底层调用的代码片段如下所示:

 试试 {//执行在 parser.prog() 处停止TimeDefParser.prog_return prog_ret = parser.prog();返回 prog_ret == null ?null : prog_ret.value;}捕获(异常前){throw new ParserException(ex.Message, ex);}

对我来说,catch (Exception) 子句应该已经捕获了任何异常.有什么理由不这样做吗?

更新:我使用 Reflector 跟踪外部组件,没有发现任何螺纹的迹象.该程序集似乎只是 ANTLR 生成的代码的运行时实用程序类.抛出的异常来自 TimeDefLexer.mTokens() 方法,其类型为 NoViableAltException,它派生自 RecognitionException -> Exception.当词法分析器无法理解流中的下一个标记时抛出此异常;换句话说,无效输入.这个异常应该发生,但是它应该被我的 try/catch 块捕获.

另外,重新抛出 ParserException 与这种情况确实无关.这是一个抽象层,在解析过程中接受任何异常并转换为我自己的 ParserException.我遇到的异常处理问题永远不会到达那行代码.事实上,我把throw new ParserException"部分注释掉了,结果还是一样.

还有一件事,我修改了有问题的原始 try/catch 块,改为捕获 NoViableAltException,从而消除了任何继承混淆.我仍然收到相同的结果.

有人曾经提出,有时 VS 在调试模式下捕获处理的异常过于活跃,但这个问题在发布模式下也会发生.

伙计,我还是被难住了!我之前没有提到过,但我运行的是 VS 2008,我所有的代码都是 3.5.外部组件是 2.0.另外,我的一些代码子类化了 2.0 程序集中的一个类.版本不匹配会导致此问题吗?

更新 2: 通过将我的 .NET 3.5 代码的相关部分移植到 .NET 2.0 项目并复制相同的场景,我能够消除 .NET 版本冲突.在 .NET 2.0 中持续运行时,我能够复制相同的未处理异常.

我了解到 ANTLR 最近发布了 3.1.所以,我从 3.0.1 升级并重试.结果是生成的代码稍微重构了一下,但在我的测试用例中出现了相同的未处理异常.

更新 3:我已经在 简化 VS 2008 项目 中复制了这个场景.您可以自行下载并检查该项目.我已经采纳了所有很棒的建议,但还没有克服这个障碍.

如果您能找到解决方法,请分享您的发现.再次感谢!

<小时>

谢谢,但 VS 2008 会在出现未处理的异常时自动中断.另外,我没有 Debug->Exceptions 对话框.抛出的 NoViableAltException 是完全有意的,旨在由用户代码捕获.由于它没有按预期捕获,程序执行意外停止作为未处理的异常.

抛出的异常是从Exception派生的,ANTLR没有进行多线程处理.

解决方案

我相信我理解问题所在.异常被捕获,问题是调试器行为的混淆以及试图重现它的每个人之间调试器设置的差异.

在您重现的第 3 种情况下,我相信您会收到以下消息:用户代码未处理 NoViableAltException"和如下所示的调用堆栈:

<前>【外码】> TestAntlr-3.1.exe!TimeDefLexer.mTokens() Line 852 + 0xe bytes C#【外码】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]}) 第 30 行 + 0xb 字节 C#【外码】

如果您在调用堆栈窗口中右键单击并运行打开显示外部代码,您会看到:

<前>Antlr3.Runtime.dll!Antlr.Runtime.DFA.NoViableAlt(int s = 0x00000000, Antlr.Runtime.IIntStream input = {Antlr.Runtime.ANTLRStringStream}) + 0x80 字节Antlr3.Runtime.dll!Antlr.Runtime.DFA.Predict(Antlr.Runtime.IIntStream input = {Antlr.Runtime.ANTLRStringStream}) + 0x21e 字节> TestAntlr-3.1.exe!TimeDefLexer.mTokens() Line 852 + 0xe bytes C#Antlr3.Runtime.dll!Antlr.Runtime.Lexer.NextToken() + 0xc4 字节Antlr3.Runtime.dll!Antlr.Runtime.CommonTokenStream.FillBuffer() + 0x147 字节Antlr3.Runtime.dll!Antlr.Runtime.CommonTokenStream.LT(int k = 0x00000001) + 0x2d 字节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]}) 第 30 行 + 0xb 字节 C#[本机到托管过渡][管理到本机过渡]mscorlib.dll!System.AppDomain.ExecuteAssembly(string assemblyFile, System.Security.Policy.Evidence assemblySecurity, string[] args) + 0x39 字节Microsoft.VisualStudio.HostingProcess.Utilities.dll!Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() + 0x2b 字节mscorlib.dll!System.Threading.ThreadHelper.ThreadStart_Context(对象状态) + 0x3b 字节mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback 回调, 对象状态) + 0x81 字节mscorlib.dll!System.Threading.ThreadHelper.ThreadStart() + 0x40 字节

调试器的消息告诉您,源自您的代码(来自 NoViableAlt)之外的异常正在通过您在 TestAntlr-3.1.exe!TimeDefLexer.mTokens() 中拥有的代码而没有被处理.

措辞令人困惑,但这并不意味着异常未被捕获.调试器让您知道您拥有的代码 mTokens()" 需要对通过它抛出的异常保持健壮.

可以玩玩的东西,看看这对于那些没有重现问题的人来说如何:

  • 转到工具/选项/调试和关闭仅启用我的代码(仅限托管)".或选项.
  • 转到调试器/异常并关闭用户未处理"通用语言运行时异常.

I'm working on a project using the ANTLR parser library for C#. I've built a grammar to parse some text and it works well. However, when the parser comes across an illegal or unexpected token, it throws one of many exceptions. The problem is that in some cases (not all) that my try/catch block won't catch it and instead stops execution as an unhandled exception.

The issue for me is that I can't replicate this issue anywhere else but in my full code. The call stack shows that the exception definitely occurs within my try/catch(Exception) block. The only thing I can think of is that there are a few ANTLR assembly calls that occur between my code and the code throwing the exception and this library does not have debugging enabled, so I can't step through it. I wonder if non-debuggable assemblies inhibit exception bubbling? The call stack looks like this; external assembly calls are in Antlr.Runtime:

    Expl.Itinerary.dll!TimeDefLexer.mTokens() Line 1213 C#
    Antlr3.Runtime.dll!Antlr.Runtime.Lexer.NextToken() + 0xfc bytes 
    Antlr3.Runtime.dll!Antlr.Runtime.CommonTokenStream.FillBuffer() + 0x22c bytes   
    Antlr3.Runtime.dll!Antlr.Runtime.CommonTokenStream.LT(int k = 1) + 0x68 bytes
    Expl.Itinerary.dll!TimeDefParser.prog() Line 109 + 0x17 bytes   C#
    Expl.Itinerary.dll!Expl.Itinerary.TDLParser.Parse(string Text = "", Expl.Itinerary.IItinerary Itinerary = {Expl.Itinerary.MemoryItinerary}) Line 17 + 0xa bytes C#

The code snippet from the bottom-most call in Parse() looks like:

     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);
     }

To me, a catch (Exception) clause should've captured any exception whatsoever. Is there any reason why it wouldn't?

Update: I traced through the external assembly with Reflector and found no evidence of threading whatsoever. The assembly seems to just be a runtime utility class for ANTLR's generated code. The exception thrown is from the TimeDefLexer.mTokens() method and its type is NoViableAltException, which derives from RecognitionException -> Exception. This exception is thrown when the lexer cannot understand the next token in the stream; in other words, invalid input. This exception is SUPPOSED to happen, however it should've been caught by my try/catch block.

Also, the rethrowing of ParserException is really irrelevant to this situation. That is a layer of abstraction that takes any exception during parse and convert to my own ParserException. The exception handling problem I'm experiencing is never reaching that line of code. In fact, I commented out the "throw new ParserException" portion and still received the same result.

One more thing, I modified the original try/catch block in question to instead catch NoViableAltException, eliminating any inheritance confusion. I still received the same result.

Someone once suggested that sometimes VS is overactive on catching handled exceptions when in debug mode, but this issue also happens in release mode.

Man, I'm still stumped! I hadn't mentioned it before, but I'm running VS 2008 and all my code is 3.5. The external assembly is 2.0. Also, some of my code subclasses a class in the 2.0 assembly. Could a version mismatch cause this issue?

Update 2: I was able to eliminate the .NET version conflict by porting relevant portions of my .NET 3.5 code to a .NET 2.0 project and replicate the same scenario. I was able to replicate the same unhandled exception when running consistently in .NET 2.0.

I learned that ANTLR has recently released 3.1. So, I upgraded from 3.0.1 and retried. It turns out the generated code is a little refactored, but the same unhandled exception occurs in my test cases.

Update 3: I've replicated this scenario in a simplified VS 2008 project. Feel free to download and inspect the project for yourself. I've applied all the great suggestions, but have not been able to overcome this obstacle yet.

If you can find a workaround, please do share your findings. Thanks again!


Thank you, but VS 2008 automatically breaks on unhandled exceptions. Also, I don't have a Debug->Exceptions dialog. The NoViableAltException that is thrown is fully intended, and designed to be caught by user code. Since it is not caught as expected, program execution halts unexpectedly as an unhandled exception.

The exception thrown is derived from Exception and there is no multi-threading going on with ANTLR.

解决方案

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.

这篇关于为什么 .NET 异常没有被 try/catch 块捕获?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆