C# 为什么 .NET 异常没有被 try/catch 块捕获?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/36014/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 09:16:34  来源:igfitidea点击:

Why is .NET exception not caught by try/catch block?

提问by spoulson

I'm working on a project using the ANTLRparser 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.

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

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:

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

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

     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?

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

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.

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

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.

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

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.

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

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

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

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?

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

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.

更新 2:通过将我的 .NET 3.5 代码的相关部分移植到 .NET 2.0 项目并复制相同的场景,我能够消除 .NET 版本冲突。在 .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.

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

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.

更新 3:我在一个简化的 VS 2008 项目中复制了这个场景。您可以自行下载并检查该项目。我已经应用了所有伟大的建议,但还没有能够克服这个障碍。

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.

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

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

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

采纳答案by Steve Steiner

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:

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

         [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.

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

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.

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

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.
  • 转到工具/选项/调试并关闭“仅启用我的代码(仅限托管)”。或选项。
  • 转到调试器/异常并关闭通用语言运行时异常的“用户未处理”。

回答by Daniel Auger

Is it possible that the exception is being thrown in another thread? Obviously your calling code is single threaded, but maybe the library you are consuming is doing some multithreaded operations under the covers.

是否有可能在另一个线程中抛出异常?显然您的调用代码是单线程的,但也许您正在使用的库在幕后进行了一些多线程操作。

回答by ljs

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:-

我同意 Daniel 的建议,异常可能发生在单独的线程上 - 尝试在 Application.ThreadException 中挂钩线程异常事件。这应该在发生任何未处理的线程异常时引发。您可以这样调整您的代码:-

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

回答by Kibbee

You can set up VS.Net to break as soon as any exception occurs. Just run your project in debug mode, and it will stop as soon as the exception is thrown. Then you should have a better idea of why it isn't being caught.

您可以将 VS.Net 设置为在发生任何异常时立即中断。只需在调试模式下运行您的项目,一旦抛出异常,它就会停止。那么你应该更好地了解为什么它没有被抓住。

Also, you can put some code in to catch all unhandled exceptions. Read the link for more info, but the basics are these two lines.

此外,您可以放入一些代码来捕获所有未处理的异常。阅读链接以获取更多信息,但基础知识是这两行。

Application.ThreadException += new ThreadExceptionEventHandler(ThreadExceptionHandler);

 // Catch all unhandled exceptions in all threads.
 AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(UnhandledExceptionHandler);

回答by ljs

Oh and in reference to what Kibbee said; if you select Debug|Exceptions in VS and just click all the boxes in the 'thrown' column it should pick everythingup AFAIK as a 'first chance exception', i.e. VS will indicate when the exception is aboutto be processed by everything else and break on the relevant code. This should help with debugging.

哦,参考 Kibbee 所说的;如果您在 VS 中选择 Debug|Exceptions 并单击“thrown”列中的所有框,它应该将 AFAIK 中的所有内容都选为“第一次机会异常”,即 VS 将指示异常即将被其他所有内容处理的时间,并且打破相关代码。这应该有助于调试。

回答by Quibblesome

"Also, you can put some code in to catch all unhandled exceptions. Read the link for more info, but the basics are these two lines."

“此外,您可以放入一些代码来捕获所有未处理的异常。阅读链接以获取更多信息,但基础是这两行。”

This is false. This used to catch all unhandled exceptions in .NET 1.0/1.1 but it was a bug and it wasn't supposed to and it was fixed in .NET 2.0.

这是错误的。这曾经在 .NET 1.0/1.1 中捕获所有未处理的异常,但它是一个错误,不应该这样做,并且在 .NET 2.0 中已修复。

AppDomain.CurrentDomain.UnhandledException 

Is only intended to be used as a last chance logging saloon so you can log the exception before the program exits. It wont catch the exception as of 2.0 onwards (although in .NET 2.0 at least there is a config value you can modify to make it act like 1.1 but it isn't recommended practice to use this.).

仅用作最后一次记录沙龙,以便您可以在程序退出之前记录异常。从 2.0 开始,它不会捕获异常(尽管在 .NET 2.0 中至少有一个配置值可以修改以使其像 1.1 一样,但不建议使用它。)。

Its worth noting that there are few exceptions that you cannotcatch, such as StackOverflowException and OutOfMemoryException. Otherwise as other people have suggested it might be an exception in a background thread somewhere. Also I'm pretty sure you can't catch some/all unmanaged/native exceptions either.

值得注意的是,有少数异常是您无法捕获的,例如 StackOverflowException 和 OutOfMemoryException。否则,正如其他人所建议的那样,它可能是某个后台线程中的一个例外。此外,我很确定您也无法捕获某些/所有非托管/本机异常。

回答by tbreffni

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:

您使用的是 .Net 1.0 还是 1.1?如果是这样,那么 catch(Exception ex) 将不会从非托管代码中捕获异常。您需要改用 catch {}。有关更多详细信息,请参阅此文章:

http://www.netfxharmonics.com/2005/10/net-20-trycatch-and-trycatchexception/

http://www.netfxharmonics.com/2005/10/net-20-trycatch-and-trycatchexception/

回答by Mark Brackett

I don't get it...your catch block just throws a new exception (with the same message). Meaning that your statement of:

我不明白……您的 catch 块只是抛出了一个新异常(具有相同的消息)。这意味着您的声明:

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.

问题是在某些情况下(不是全部)我的 try/catch 块不会捕获它,而是作为未处理的异常停止执行。

is exactly what is expectedto happen.

正是预期会发生的事情。

回答by flipdoubt

I agree with Daniel Augerand kronozthat this smells like an exception that has something to do with threads. Beyond that, here are my other questions:

我同意Daniel Augerkronoz 的观点,这听起来像是一个与线程有关的例外。除此之外,这是我的其他问题:

  1. What does the complete error message say? What kind of exception is it?
  2. Based on the stack trace you've provided here, isn't the exception thrown by you code in TimeDefLexer.mTokens()?
  1. 完整的错误消息是什么意思?什么样的例外?
  2. 根据您在此处提供的堆栈跟踪,您在 TimeDefLexer.mTokens() 中的代码抛出的异常不是吗?

回答by Scott Dorman

The best option sounds like setting Visual Studio to break on all unhandled exceptions (Debug -> Exceptions dialog, check the box for "Common Language Runtime Exceptions" and possibly the others as well). Then run your program in debug mode. When the ANTLR parser code throws an exception it should be caught by Visual Studio and allow you to see where it is occurring, the exception type, etc.

最好的选择听起来像是将 Visual Studio 设置为中断所有未处理的异常(调试 -> 异常对话框,选中“公共语言运行时异常”框以及其他可能的框)。然后在调试模式下运行你的程序。当 ANTLR 解析器代码抛出异常时,它应该被 Visual Studio 捕获并允许您查看它发生的位置、异常类型等。

Based on the description, the catch block appears to be correct, so one of several things could be happening:

根据描述,catch 块似乎是正确的,因此可能会发生以下几种情况之一:

  1. the parser is not actually throwing an exception
  2. the parser is ultimately throwing something that isn't deriving from System.Exception
  3. there is an exception being thrown on another thread that isn't being handled
  1. 解析器实际上并没有抛出异常
  2. 解析器最终抛出的东西不是从 System.Exception 派生的
  3. 在另一个未被处理的线程上抛出异常

It sounds like you have potentially ruled out issue #3.

听起来您可能已经排除了问题 #3。

回答by Orion Edwards

I traced through the external assembly with Reflector and found no evidence of threading whatsoever.

我用 Reflector 追踪外部组件,没有发现任何螺纹的迹象。

You can't find any threading does not mean there is no threading

找不到任何线程并不意味着没有线程

.NET has a 'thread pool' which is a set of 'spare' threads that sit around mostly idle. Certain methods cause things to run in one of the thread pool threads so they don't block your main app.

.NET 有一个“线程池”,它是一组主要处于空闲状态的“备用”线程。某些方法会导致事情在线程池线程之一中运行,因此它们不会阻塞您的主应用程序。

The blatant examples are things like ThreadPool.QueueUserWorkItem, but there are lots and lots of other things which can also run things in the thread pool that don't look so obvious, like Delegate.BeginInvoke

明显的例子是像ThreadPool.QueueUserWorkItem这样的东西,但是还有很多其他东西也可以在线程池中运行看起来不那么明显的东西,比如Delegate.BeginInvoke

Really, you need to do what kibbee suggests.

真的,你需要按照kibbee 的建议去做