重新抛出异常时,通常的建议是使用throw;
语句,以便保留原始堆栈跟踪。 (Example)
但是,当我尝试这个简单的示例时,Visual Studio调试器不会显示原始堆栈跟踪。
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.
}
}
}
}
如何使用调试器查找异常的原始来源?
答案 0 :(得分:9)
您最好的选择是让Visual Studio打破原始异常,而不是从堆栈跟踪导航回来。要做到这一点:
1)单击“Debug”菜单项 2)点击“例外...” 3)选择“Common Language Runtime Exceptions” - “Thrown”
使用这种方法,如果抛出许多异常,您可能会得到比您真正想要的更多。您可以通过展开树列表来过滤它中断的异常。
见图:
答案 1 :(得分:7)
如果您正在运行Visual Studio 2010 Ultimate,use IntelliTrace。
它记录所有抛出的异常,并允许您“及时调试”以查看每次抛出时的参数,线程和变量。
答案 2 :(得分:1)
我找到的最佳解决方案是将Exception
callstack写入Debug.Console
,然后让Visual Studio中的内置代码行解析器提供导航。
我发现在处理 AppDomain 和 WPF Dispatcher 上的未处理异常时非常有用,因为Visual Studio总是打破得太晚。
基于Code Project上的一篇文章,我修改了它作为单个文本块输出到 Console - 而不是逐行 - 这是必要的我记录还写入控制台。
<强>用法强>
public void ReportException(Exception exception)
{
if (Debugger.IsAttached)
{
DebugHelper.PrintExceptionToConsole(exception);
Debugger.Break();
}
// ...
}
<强>来源
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();
}
}
要使用上述内容,您还需要下面的扩展方法,并为System.CodeDom.Compiler
添加IndentedTextWriter
命名空间。
扩展方法
/// <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();
}
答案 3 :(得分:1)
您可以使用DebuggerNonUserCode
属性。
请参阅http://blogs.msdn.com/b/jmstall/archive/2007/02/12/making-catch-rethrow-more-debuggable.aspx
示例如下:
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.
}
}
}
}
答案 4 :(得分:0)
顺便说一句,在vb.net中,可以在某些情况下使用异常过滤器,其中一个人知道一个人真的不想捕获异常 - 只是发现它发生了。如果代码是在vb.net中编写的并且使用过滤器来捕获异常(可能在'finally'块中执行输出),则不会出现“catch和rethrow” - 光标会跳转到源代码原始异常(作为奖励,vs会在任何堆栈展开之前中断程序流程)。请注意,每次抛出特定异常时都可以选择使用vs trap,无论是否会被捕获,但有时候只会抛出一些抛出异常的地方而不是所有异常。