在.net Exception中如何获取带有参数值的堆栈跟踪

时间:2008-10-01 14:33:07

标签: c# .net exception

我正在尝试在.net(c#)中添加一个未处理的异常处理程序,该处理程序对于'user'应该尽可能有用。最终用户大多是程序员,所以他们只需要暗示他们操纵错误的对象。

我正在开发一个类似于Windows XP错误报告的窗口,当应用程序崩溃但是尽可能多地提供关于抛出的异常的中间信息。

虽然堆栈跟踪使我(因为我有源代码)能够查明问题的根源,但是用户没有它,因此它们在没有进一步信息的情况下丢失。不用说我必须花很多时间来支持这个工具。

有一些系统异常,例如Dictionary集合抛出的KeyNotFoundException,它真的让我感到烦恼,因为它们没有在消息中包含未找到的密钥。我可以用大量的try catch块来填充我的代码,但它相当激烈,需要维护更多的代码,更不用说更多的字符串必须最终被本地化。

最后一个问题:有没有办法获得(在运行时)调用堆栈跟踪中每个函数的参数值? 仅这一点就可以解决90%的支持电话。

11 个答案:

答案 0 :(得分:7)

我不认为System.Diagnostics.StackFrame提供参数信息(方法签名除外)。

您可以通过AOP跟踪日志记录来检测麻烦的调用,甚至可以使用其异常拦截功能有条件地记录而不必丢弃您的代码。浏览http://www.postsharp.org/

答案 1 :(得分:6)

同样,我没有找到任何在运行时自动派生参数的东西。相反,我使用Visual Studio加载项来生成明确打包参数的代码,如下所示:

public class ExceptionHandler
{
    public static bool HandleException(Exception ex, IList<Param> parameters)
    {
        /*
         * Log the exception
         * 
         * Return true to rethrow the original exception,
         * else false
         */
    }
}

public class Param
{
    public string Name { get; set; }
    public object Value { get; set; }
}

public class MyClass
{
    public void RenderSomeText(int lineNumber, string text, RenderingContext context)
    {
        try
        {
            /*
             * Do some work
             */
            throw new ApplicationException("Something bad happened");
        }
        catch (Exception ex)
        {
            if (ExceptionHandler.HandleException(
                    ex, 
                    new List<Param>
                    {
                        new Param { Name = "lineNumber", Value=lineNumber },
                        new Param { Name = "text", Value=text },
                        new Param { Name = "context", Value=context}
                    }))
            {
                throw;
            }
        }
    }
}

编辑:或者,通过将HandleException参数设为params数组:

public static bool HandleException(Exception ex, params Param[] parameters)
{
   ...
}

...
if (ExceptionHandler.HandleException(
                    ex, 
                    new Param { Name = "lineNumber", Value=lineNumber },
                    new Param { Name = "text", Value=text },
                    new Param { Name = "context", Value=context}
                    ))
{
    throw;
}
...

生成额外代码以将参数显式传递给异常处理程序有点痛苦,但是使用加载项至少可以自动化它。

自定义属性可用于注释您不希望加载项传递给异常处理程序的任何参数:

public UserToken RegisterUser( string userId, [NoLog] string password )
{
}

2ND编辑:

请注意,我完全忘记了AVICode:

http://www.avicode.com/

他们使用呼叫拦截技术来提供这种信息,因此必须是可能的。

答案 2 :(得分:3)

不幸的是,除了实际附加到应用程序的调试工具之外,您无法从callstack获取参数的实际值。但是,通过使用System.Diagnostics中的StackTrace和StackFrame对象,您可以遍历调用堆栈并读出所有调用的方法以及参数名称和类型。你会这样做:

System.Diagnostics.StackTrace callStack = new System.Diagnostics.StackTrace();
System.Diagnostics.StackFrame frame = null;
System.Reflection.MethodBase calledMethod = null;
System.Reflection.ParameterInfo [] passedParams = null;
for (int x = 0; x < callStack.FrameCount; x++)
{
    frame = callStack.GetFrame(x);
    calledMethod = frame.GetMethod();
    passedParams = calledMethod.GetParameters();
    foreach (System.Reflection.ParameterInfo param in passedParams)
        System.Console.WriteLine(param.ToString()); 
}

如果您需要实际值,那么您将需要采取小型泵并分析它们,我担心。有关获取转储信息的信息,请访问:

http://www.debuginfo.com/tools/clrdump.html

答案 3 :(得分:2)

有一个来自redgate的软件工具,看起来非常有用。

http://www.red-gate.com/products/dotnet-development/smartassembly/

目前我们正在通过eMail收集客户的错误报告,我有时会遇到一些重要的数据缺失(主要是一些非常基本的变量,比如当前记录中的id,所以我可以重现这个bug)。我还没有测试过这个工具,但根据我的理解,它会从堆栈中收集参数值和局部变量。

答案 4 :(得分:1)

我不相信有内置机制。检索堆栈跟踪的每个帧,同时允许您确定跟踪中的方法,仅提供有关该方法的反射类型信息。没有参数信息。我认为这就是为什么有些例外,特别是ArgumentException等。人。提供一种机制来指定异常中涉及的参数的值,因为没有简单的方法来获取它。

答案 5 :(得分:1)

由于最终用户是开发人员,因此您可以为他们提供一个版本,该版本可以记录传递的所有键值/参数。并提供一个设施,以便他们可以打开/关闭日志记录。

答案 6 :(得分:1)

理论上可以通过利用可移植可执行文件(PE)文件格式来获取变量类型和偏移量来做你想要的事情,但是几年前我遇到了试图这样做的[文档]墙。 。祝你好运!

答案 7 :(得分:1)

如果你可以做你正在寻找的东西,你就会打败.NET安全的一个组成部分。

在这种情况下的最佳选择,它附加到调试器或分析器(它们中的任何一个都可以访问这些值)。前者可以附加,后者需要在程序启动前激活。

答案 8 :(得分:0)

我不知道这样的事情,但我自己也想要它。我通常在抛出异常时自己做,但如果不是你扔掉,那么你可能会和我们其他人一起出去。

答案 9 :(得分:0)

我不知道在调用堆栈跟踪中获取每个函数的参数值的方法,但是一个解决方案是捕获特定的异常(在您的示例中为KeyNotFoundException)并重新抛出它在一个新的例外中。这允许您关联您希望的任何其他信息

例如:

Dim sKey as String = "some-key"
Dim sValue as String = String.Empty

Try
   sValue = Dictionary(sKey)
Catch KeyEx As KeyNotFoundException
   Throw New KeyNotFoundException("Class.Function() - Couldn't find [" & sKey & "]", KeyEx)
End Try

我感谢你关于错误字符串本地化的陈述,但是如果你的读者是“大多数程序员”,那么惯例已经在某种程度上决定了对英语的理解(正确或错误 - 但那是另一场辩论!)

答案 10 :(得分:0)

一旦你遇到异常,你感兴趣的两件事就是System.Diagnostics.StackTrace和System.Diagnostics.StackFrame

有一个MSDN示例here