为什么.NETexception不被try / catch块捕获?

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

我的问题是,我不能在其他地方复制这个问题,但在我的完整代码。 调用堆栈显示exception肯定发生在我的try / catch(Exception)块中。 我唯一能想到的是在我的代码和引发exception的代码之间发生了几个ANTLR程序集调用,而且这个库没有启用debugging,所以我不能一步一步地完成它。 我不知道如果不可debugging程序集抑制exception冒泡? 调用堆栈看起来像这样; 外部程序集调用在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()第109行+ 0x17字节C#
     Expl.Itinerary.dll!Expl.Itinerary.TDLParser.Parse(string Text =“”,Expl.Itinerary.IItinerary Itinerary = {Expl.Itinerary.MemoryItinerary})Line 17 + 0xa bytes C#

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

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

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

而且,ParserException的重新抛出与这种情况无关。 这是一个抽象层,在parsing和转换为我自己的ParserException期间会采取任何exception。 我遇到的exception处理问题永远不会达到这一行代码。 事实上,我评论说“抛出新的ParserException”部分,仍然收到了同样的结果。

还有一件事,我修改了原来的try / catch块,而不是捕获NoViableAltException,消除了inheritance的混淆。 我仍然收到相同的结果。

有人曾经提出,在debugging模式下,有时VS在捕获处理的exception时过于活跃,但是在发布模式下也会发生这个问题。

男人,我仍然难倒! 我以前没有提到过,但是我运行的是VS 2008,我的代码都是3.5。 外部组件是2.0。 此外,我的一些代码在2.0汇编中是一个类。 版本不匹配会导致此问题吗?

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

我了解到ANTLR最近发布了3.1。 所以,我从3.0.1升级并重试。 事实certificate,生成的代码是有点重构,但在我的testing用例中发生相同的未处理的exception。

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

如果您能find解决办法,请分享您的发现。 再次感谢!


谢谢,但VS 2008自动打破未处理的exception。 另外,我没有Debug-> Exceptions对话框。 抛出的NoViableAltException完全是打算的,并且被devise为被用户代码捕获。 由于未按预期方式捕获,程序执行意外停止,因为未处理的exception。

抛出的exception是从Exception派生的,并且ANTLR没有进行multithreading。

我相信我明白这个问题。 正在捕获的exception,问题是debugging器的行为和每个试图重新编译的人之间的debugging器设置的差异的混淆。

在你的repro的第三种情况下,我相信你会得到以下消息:“NoViableAltException被用户代码解除了操作”和一个如下所示的调用堆栈:

          [外部代码]    
     > TestAntlr-3.1.exe!TimeDefLexer.mTokens()852 + 0xe字节C#
         [外部代码] 
         TestAntlr-3.1.exe!TimeDefParser.prog()行141 + 0x14字节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#
         [外部代码] 

如果你右键单击callstack窗口并运行打开显示外部代码,你会看到:

         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 bytes  
     > TestAntlr-3.1.exe!TimeDefLexer.mTokens()852 + 0xe字节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()行141 + 0x14字节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#
         [原生到托pipe转换]  
         [pipe理到本地过渡]  
         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.ContextCallbackcallback,对象状态)+ 0x81字节    
         mscorlib.dll!System.Threading.ThreadHelper.ThreadStart()+ 0x40字节

debugging器的消息告诉你,源代码之外的exception(来自NoViableAlt)正在经历TestAntlr-3.1.exe!TimeDefLexer.mTokens()中的代码而不被处理。

措辞很混乱,但并不意味着这个例外是没有被捕获的。 debugging器让你知道你拥有mTokens()的代码需要强大的反对这个exception抛出。

要玩的东西,看看这是如何看待那些没有重现问题的人:

  • 转到工具/选项/debugging,并closures“启用只是我的代码(只pipe理)”。 或选项。
  • 转到debugging器/例外,并closures“用户未处理”的公共语言运行时exception。

无论程序集是否被编译为发布版本,exception应该“调出”给调用者,没有理由不在debugging模式下编译的程序集应该对其产生任何影响。

我同意Daniel的build议,也许这个exception发生在一个单独的线程上 – 尝试挂钩Application.ThreadException中的线程exception事件。 当任何未处理的线程exception发生时,这应该被引发。 你可以这样调整你的代码:

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

你使用的是.Net 1.0还是1.1? 如果是这样的话catch(Exception ex)将不会捕获来自非托pipe代码的exception。 您需要使用catch {}。 进一步的细节见这篇文章:

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

是否有可能在另一个线程中抛出exception? 显然你的调用代码是单线程的,但是你正在使用的库可能是在做一些multithreading的操作。

我与@Shaun Austin–尝试用完全合格的名字包装这个尝试

 catch (System.Exception) 

看看是否有帮助。是否ANTLR文档说什么exception应该抛出?

就我个人而言,我并不相信线程理论。

有一次,我曾经见过这个,我正在和一个定义Exception的库一起工作,我的意思是说,实际的Catch是指一个不同的“Exception”types(如果它是完全合格的,那就是Company。 Lib.Exception,但它不是因为使用),所以当涉及到捕获正在抛出的正常exception(某些types的参数exception,如果我没有记错的话),它只是不会捕获它,因为types不匹配。

那么总而言之,在另一个名称空间中是否有另一个Exceptiontypes在该类中使用?

编辑:一个快速的方法来检查这是确保在你的catch子句中,你完全限定exceptiontypes为“System.Exception”,并给它一个旋风!

编辑2:好吧,我已经尝试了代码,并承认失败了。 如果没有人提出解决scheme,我将不得不在早上再看一遍。

我可以告诉你这里发生了什么…

Visual Studio打破了,因为它认为exception是未处理的。 什么没有处理意味着什么? 那么,在Visual Studio中,在工具…选项中有一个设置…debugging…常规…“启用只是我的代码(仅pipe理)”。 如果选中此选项,并且exception传播出代码并传出到与“NOT YOUR CODE”程序集(例如Antlr)中的程序调用相关联的堆栈框架,则将其视为“未处理”。 由于这个原因,我closures了“启用我的代码”function。 但是,如果你问我,这是蹩脚的…让我们说你这样做:

 ExternalClassNotMyCode c = new ExternalClassNotMyCode(); try { c.doSomething( () => { throw new Exception(); } ); } catch ( Exception ex ) {} 

doSomething调用你的匿名函数,那个函数抛出一个exception…

请注意,如果启用“仅启用我的代码”,则这是根据Visual Studio的“未处理的exception”。 此外,请注意,在debugging模式下,它停止,就好像它是断点一样,但在非debugging或生产环境中,代码是完全有效的,并按预期工作。 另外,如果你只是在debugging器中“继续”,应用程序继续快乐的方式(它不会停止线程)。 它被认为是“未处理的”,因为exception通过不在你的代码(即在外部库)中的栈帧传播。 如果你问我,这是糟糕的。 请更改此默认行为Microsoft。 这是使用exception来控制程序逻辑的完美有效的例子。 有时,你不能改变第三方库的行为,这是一个非常有用的方法来完成许多任务。

以MyBatis为例,您可以使用此技术来停止处理正在通过调用SqlMapper.QueryWithRowDelegate正在收集的logging。

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

我能想到的唯一可能性就是别的东西在你面前捕捉,并以一种似乎是未被捕获的exception(例如退出过程)的方式处理它。

我的try / catch块不会捕获它,而是停止执行作为一个未处理的exception。

你需要find导致退出过程的原因。 这可能是除了未处理的exception之外的东西。 您可以尝试使用在“{,, kernel32.dll} ExitProcess”上设置断点的本地debugging器。 然后使用SOS确定哪些托pipe代码正在调用退出进程。

嗯,我不明白这个问题。 我下载并尝试您的示例解决scheme文件。

在TimeDefLexer.cs行852中引发了一个exception,随后由Program.cs中的catch块处理,这只是说明Handledexception

如果我取消注释上面的catch块,它将进入该块。

这里似乎是什么问题?

正如Kibbee所说,Visual Studio将停止exception,但是如果您要求继续,exception将会被您的代码所捕获。

我下载了样本VS2008项目,也有点难倒了。 然而,我能够克服例外情况,尽pipe可能不会对你有好处。 但是,我发现:

这个邮件列表文章讨论了你所遇到的同样的问题。

从那里,我在主program.cs文件中添加了几个虚拟类:

 class MyNoViableAltException : Exception { public MyNoViableAltException() { } public MyNoViableAltException(string grammarDecisionDescription, int decisionNumber, int stateNumber, Antlr.Runtime.IIntStream input) { } } class MyEarlyExitException : Exception { public MyEarlyExitException() { } public MyEarlyExitException(int decisionNumber, Antlr.Runtime.IIntStream input) { } } 

然后将使用行添加到TimeDefParser.cs和TimeDefLexer.cs中:

 using NoViableAltException = MyNoViableAltException; using EarlyExitException = NoViableAltException; 

这样,exception就会冒泡到假的exception类中,并且可以在那里处理,但是在TimeDefLexer.cs的mTokens方法中仍然有一个exception。 包装,在该类尝试捕获抓住了例外:

  try { alt4 = dfa4.Predict(input); } catch { } 

我真的不明白为什么要用内部方法来包装它,而不是在线程不在场的情况下处理错误的地方,而是希望能够指出一个比我更聪明的人在正确的方向。

我下载了你的代码,一切按预期工作。

Visual Studiodebugging器正确地拦截所有exception。 捕捉块按预期工作。

我正在运行Windows 2003 Server SP2,VS2008 Team Suite(9.0.30729.1 SP)

我试图编译你的项目的.NET 2.0,3.0和3.5

@Steve Steiner,您提到的debugging器选项与此行为无关。

我试图玩这些选项,没有明显的效果 – 抓住块设法拦截所有的例外。

史蒂夫·斯坦纳(Steve Steiner)是正确的,例外是源自antlr库,通过mTokens()方法并被捕获到antlr库中。 问题是这个方法是由antlr自动生成的。 因此,在生成parsing器/词法分析器类时,将会覆盖mTokens()中处理exception的任何更改。

默认情况下,antlr会logging错误并尝试恢复parsing。 你可以重写这个,这样parser.prog()会在遇到错误的时候抛出一个exception。 从你的示例代码,我认为这是你所期待的行为。

将此代码添加到您的语法(.g)文件。 您还需要在debugging菜单中closures“启用我的代码”。

 @members { public override Object RecoverFromMismatchedSet(IIntStream input,RecognitionException e, BitSet follow) { throw e; } } @rulecatch { catch (RecognitionException e) { throw e; } } 

这是我在“定义ANTLR参考”一书的“退出第一个错误的识别器”一章给出的C#版本的尝试。

希望这是你正在寻找的。

一旦发生exception,您可以立即设置VS.Net。 只要在debugging模式下运行你的项目,一旦抛出exception就会停下来。 那么你应该有一个更好的主意,为什么不被抓到。

此外,你可以把一些代码来捕获所有未处理的exception 。 阅读更多信息的链接,但基本是这两行。

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

哦,关于Kibbee所说的, 如果你在VS中selectDebug | Exceptions并且点击'抛出'列中的所有框,它应该 AFAIK中的所有东西都作为'第一次机会exception',也就是说VS将指示什么时候exception将被其他东西处理,打破相关的代码。 这应该有助于debugging。

最好的select听起来像设置Visual Studio打破所有未处理的exception(debugging – >exception对话框,选中“公共语言运行时exception”,也可能是其他人)。 然后在debugging模式下运行你的程序。 当ANTLRparsing器代码抛出一个exception时,它应该被Visual Studio捕获,并允许你看到它发生的地方,exceptiontypes等等。

根据描述,catch块看起来是正确的,所以有几件事情可能会发生:

  1. parsing器实际上并没有抛出exception
  2. parsing器最终抛出一些不是从System.Exception派生的东西
  3. 在另一个没有被处理的线程上抛出exception

这听起来像你有可能排除问题#3。

我用reflection器追踪外部组件,没有发现任何穿线的迹象。

你不能find任何线程并不意味着没有线程

.NET有一个“线程池”,它是一组“闲置”线程,大部分空闲。 某些方法会导致事情在其中一个线程池线程中运行,因此它们不会阻止您的主应用程序。

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

真的,你需要做什么kibbeebuild议 。

你是否试图在catch子句中打印(Console.WriteLine())exception,而不是使用visual studio并在控制台上运行你的应用程序?

我相信史蒂夫·斯坦纳是正确的。 在研究Steve的build议时,我在“工具”|“选项”|“debugging器”|“常规”中find了关于“启用只是我的代码”的选项。 build议在非用户代码抛出或处理exception时,debugging器将在某些情况下中断。 我不确定为什么这很重要,或者为什么debugging器明确地说这个exception是真的没有处理的。

我能够通过禁用“启用只是我的代码”选项来消除虚假中断。 这也通过移除“用户处理”列来更改“debugging|例外”对话框,因为它不再适用。 或者,您可以取消选中CLR的“用户处理”框并获得相同的结果。

Bigtime感谢大家的帮助!

“另外,你可以放一些代码来捕获所有未处理的exception,请阅读链接了解更多信息,但基本知识就是这两行。

这是错误的。 这用于捕获在.NET 1.0 / 1.1中的所有未处理的exception,但它是一个错误,它不应该在.NET 2.0中修复。

 AppDomain.CurrentDomain.UnhandledException 

仅用作login轿车的最后机会,以便在程序退出之前loggingexception。 它不会捕捉2.0以上的exception(虽然在.NET 2.0中,至less有一个configuration值可以修改,使其像1.1一样行事,但不build议使用这种做法)。

值得注意的是,有几个例外,你不能赶上,如StackOverflowException和OutOfMemoryException。 否则,正如其他人所说,它可能是一个在某个地方的后台线程的例外。 另外我敢肯定,你不能捕捉一些/所有非托pipe/本地exception。

我不明白…你的catch块只是抛出一个新的exception(与相同的消息)。 这意味着你的陈述:

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

正是预期发生的事情。

我同意Daniel Auger和kronoz的说法 ,这个味道就像是一个与线程有关的exception。 除此之外,这是我的其他问题:

  1. 什么是完整的错误信息说? 什么样的例外呢?
  2. 根据你在这里提供的堆栈跟踪,是不是你在TimeDefLexer.mTokens()中的代码引发的exception?

我不知道如果我不清楚,但如果是这样,我看到debugging器暂停执行与NoViableAltExceptiontypes的“未处理的exception”。 最初,我对这个Debug-> Exceptions菜单项一无所知,因为MS希望在VS安装的时候,当你不知道它们有什么不同时,可以提交一个configuration文件。 显然, 我没有在C#开发configuration文件,并没有这个选项 。 在最后debugging了所有抛出的CLRexception之后,我不幸发现了任何导致这个未处理的exception问题的原因的新行为。 抛出的所有exception都是预期的,并且应该在try / catch块中处理。

我审查了外部程序集,没有multithreading的证据。 那么,我的意思就是没有提到System.Threading,没有任何代表被使用。 我熟悉这构成实例化一个线程。 我通过在未处理的exception时观察线程工具箱来validation这一点,以查看只有一个正在运行的线程。

我和ANTLR的人有一个公开的问题,所以也许他们以前能够解决这个问题。 我已经能够在VS 2008和VS 2005下使用.NET 2.0和3.5在简单的控制台应用程序项目中复制它。

这只是一个痛点,因为它强制我的代码只能使用已知的有效parsing器input。 如果基于用户input引发未处理的exception,则使用IsValid()方法会有风险。 当有更多的人了解到这个问题时,我会把这个问题保持更新。

@spoulson,

如果你可以复制它,你可以把它张贴在某个地方吗? 你可以尝试的一个途径是使用SOS扩展名WinDBG来运行应用程序并捕获未处理的exception。 它将在第一次机会exception(在运行时试图查找处理程序之前)中断,并且您可以在那一刻看到它来自哪里,以及哪个线程。

如果你之前没有使用过WinDBG,可能会有些压倒性的,但是这里有一个很好的教程:

http://blogs.msdn.com/johan/archive/2007/11/13/getting-started-with-windbg-part-i.aspx

一旦你启动了WinDBG,你可以通过Debug-> Event Filters来切换未处理的exception。

哇,到目前为止的报道中,有2个是正确的,1个是我遇到的问题。 什么版本的Windows,Visual Studio使用和.NET框架与内部版本号?

我正在运行XP SP2,VS 2008 Team Suite(9.0.30729.1 SP),C#2008(91899-270-92311015-60837)和.NET 3.5 SP1。

如果您使用com对象您的项目和尝试catch块不捕获exception,您将需要禁用工具/debugging/中断例外跨AppDomain或pipe理/本地边界(只pipe理)选项。