繁体   English   中英

查找最初使用Visual Studio C#debugger抛出rethrown异常的位置?

[英]Find where rethrown exception was originally thrown using Visual Studio C# debugger?

重新抛出异常时通常的建议是使用throw; 声明,以便保留原始堆栈跟踪。 例子

但是,当我尝试这个简单的示例时,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.
            }
        }
    }
}

如何使用调试器查找异常的原始来源?

您最好的选择是让Visual Studio打破原始异常,而不是从堆栈跟踪导航回来。 去做这个:

1)单击“Debug”菜单项2)单击“Exceptions ...”3)选择“Common Language Runtime Exceptions” - “Thrown”

使用这种方法,如果抛出许多异常,您可能会得到比您真正想要的更多。 您可以通过展开树列表来过滤它中断的异常。

见图:

在此输入图像描述

如果您正在运行Visual Studio 2010 Ultimate,请使用IntelliTrace

它记录所有抛出的异常,并允许您“及时调试”以查看每次抛出时的参数,线程和变量。

(摘自Chris Schmich对类似问题的回答 。)

您可以使用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.
            }
        }
    }
}

我发现的最佳解决方案是将Exception callstack写入Debug.Console ,然后让Visual Studio中的内置代码行解析器提供导航。

我发现它在处理AppDomainWPF Dispatcher上的未处理异常时非常有用,因为Visual Studio总是打破得太晚。

根据关于代码项目的文章,我修改了它作为单个文本块输出到控制台 - 而不是逐行 - 这是我必须记录也写入控制台的必要条件。

用法

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

}

要使用上面的内容,您还需要下面的扩展方法,并为IndentedTextWriter添加System.CodeDom.Compiler命名空间。

扩展方法

/// <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和rethrow” - 光标会跳转到源代码原始异常(作为奖励,vs会在任何堆栈展开之前中断程序流程)。 请注意,每次抛出特定异常时都可以选择使用vs trap,无论是否会被捕获,但有时候只会抛出一些抛出异常的地方而不是所有异常。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM