Find where rethrown exception was originally thrown using Visual Studio C# debugger?
当重新引发异常时,通常的建议是使用
但是,当我尝试这个简单的示例时,Visual Studio调试器不显示原始堆栈跟踪。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | namespace ExceptionTest { class Program { static void ThrowException() { throw new System.Exception(); // The line that I WANT the debugger to show. } static void Main(string[] args) { try { ThrowException(); } catch (System.Exception) { System.Console.WriteLine("An exception was thrown."); throw; // The line that the debugger ACTUALLY shows. } } } } |
如何使用调试器查找异常的原始源?
最好的选择是要求Visual Studio打破原始异常,而不是从堆栈跟踪导航回它。这样做:
1)单击"调试"菜单项2)单击"例外…"3)选择"公共语言运行时异常"-"引发"
使用这种方法,如果抛出许多异常,您可能会得到比实际需要更多的结果。您可以通过展开树列表来筛选它破坏的异常。
见图像:
如果运行的是Visual Studio 2010旗舰版,请使用IntelliTrace。
它保存所有抛出异常的记录,并允许您"及时调试",以便在每次抛出时查看参数、线程和变量。
(摘自克里斯·施密奇对类似问题的回答。)
您可以使用
请参阅http://blogs.msdn.com/b/jmstall/archive/2007/02/12/making-catch-rethrow-more-debuggable.aspx
示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | namespace ExceptionTest { class Program { static void ThrowException() { throw new System.Exception(); // The line that I WANT the debugger to show. } [DebuggerNonUserCode()] static void Main(string[] args) { try { ThrowException(); } catch (System.Exception) { System.Console.WriteLine("An exception was thrown."); throw; // The line that the debugger ACTUALLY shows. } } } } |
我找到的最佳解决方案是将
我发现它在处理AppDomain和WPF调度程序上未处理的异常时非常有用,因为Visual Studio总是太迟中断。
基于一篇关于代码项目的文章,我修改了它,它以单个文本块(而不是一行一行)的形式输出到控制台,这是必需的,我还将日志记录写入控制台。
用法
1 2 3 4 5 6 7 8 9 10 11 | public void ReportException(Exception exception) { if (Debugger.IsAttached) { DebugHelper.PrintExceptionToConsole(exception); Debugger.Break(); } // ... } |
来源
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | public static class DebugHelper { // Original idea taken from the CodeProject article // http://www.codeproject.com/Articles/21400/Navigating-Exception-Backtraces-in-Visual-Studio private static readonly string StarSeparator = new String('*', 80); private static readonly string DashSeparator = new String('-', 80); private const string TabString =" "; /// <summary> /// Prints the exception using a format recognized by the Visual Studio console parser. /// Allows for quick navigation of exception call stack. /// </summary> /// <param name="exception">The exception.</param> public static void PrintExceptionToConsole(Exception exception) { using (var indentedTextWriter = new IndentedTextWriter(Console.Out, TabString)) { var indentLevel = 0; while (exception != null) { indentedTextWriter.Indent = indentLevel; indentedTextWriter.Write(FormatExceptionForDebugLineParser(exception)); exception = exception.InnerException; indentLevel++; } } } private static string FormatExceptionForDebugLineParser(Exception exception) { StringBuilder result = new StringBuilder(); result.AppendLine(StarSeparator); result.AppendLineFormat(" {0}: "{1}"", exception.GetType().Name, exception.Message); result.AppendLine(DashSeparator); // Split lines into method info and filename / line number string[] lines = exception.StackTrace.Split(new string[] {" at" }, StringSplitOptions.RemoveEmptyEntries) .Select(x => x.Trim()) .Where(x => !String.IsNullOrEmpty(x)) .ToArray(); foreach (var line in lines) { string[] parts = line.Split(new string[] {" in" }, StringSplitOptions.RemoveEmptyEntries); string methodInfo = parts[0]; if (parts.Length == 2) { string[] subparts = parts[1].Split(new string[] {":line" }, StringSplitOptions.RemoveEmptyEntries); result.AppendLineFormat(" {0}({1},1): {2}", subparts[0], Int32.Parse(subparts[1]), methodInfo); } else result.AppendLineFormat(" {0}", methodInfo); } result.AppendLine(StarSeparator); return result.ToString(); } } |
要使用上述方法,您还需要下面的扩展方法,并为
扩展方法
1 2 3 4 5 6 7 8 9 10 11 | /// <summary> /// Appends the string returned by processing a composite format string followed by the default line terminator. /// </summary> /// <param name="sb">The StringBuilder.</param> /// <param name="format">The format.</param> /// <param name="args">The args.</param> public static void AppendLineFormat(this StringBuilder sb, string format, params object[] args) { sb.AppendFormat(format, args); sb.AppendLine(); } |
顺便说一句,在vb.net中,我们可以在这样的情况下使用异常过滤器,在这种情况下,我们知道自己对捕获异常并不真正感兴趣——只是发现它发生了。如果代码是用vb.net编写的,并使用过滤器捕获异常(可能是在"finally"块中执行输出本身),则不会出现"catch and rethrow"--光标将跳转到原始异常的源(作为额外的好处,vs将在任何堆栈展开之前中断程序流)。请注意,每次抛出一个特定的异常时,无论它是否会被捕获,都可以选择使用vs陷阱,但有时,人们只对抛出异常的某些位置感兴趣,而不是对所有位置感兴趣。