在.net异常中,如何获取具有参数值的堆栈

本文关键字:参数 堆栈 获取 异常 net 何获取 | 更新日期: 2023-09-27 17:48:53

我正在尝试在.net(c#)中添加一个未处理的异常处理程序,该处理程序应该对"用户"尽可能有用。最终用户大多是程序员,所以他们只需要知道他们操作错了什么对象。

我正在开发一个类似于应用程序崩溃时windows XP错误报告的窗口,但它会立即提供尽可能多的关于抛出的异常的信息。

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

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

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

在.net异常中,如何获取具有参数值的堆栈

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

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

同样,我还没有发现任何可以在运行时自动导出参数的东西。相反,我使用了一个VisualStudio插件来生成明确打包参数的代码,如下所示:

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

EDIT:或者,通过将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 )
{
}

第二次编辑:

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

http://www.avicode.com/

他们使用呼叫拦截技术来准确地提供这种信息,所以这一定是可能的。

不幸的是,除了实际连接到应用程序的调试工具之外,您无法从调用堆栈中获得参数的实际值。但是,通过使用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

有一个来自redgate的软件工具,看起来很有前景。

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

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

如果你能做到你想要的,你将击败.NET安全的一个组成部分。

在这种情况下,最好的选择是附加到调试器或探查器(它们中的任何一个都可以访问这些值)。前者可以附加,后者需要在程序启动前处于活动状态。

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

理论上可以利用可移植可执行文件(PE)格式来获取变量类型和偏移量,但几年前,我在尝试这样做时遇到了一堵[文件]墙。祝你好运

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

我不知道有什么类似的事情,但我自己也想要。抛出异常时,我通常自己会这么做,但如果不是你的,那么你可能会和我们其他人一起出局。

一旦你有了异常,你感兴趣的两件事就是System.Diagnostics.StackTrace和System.Diagnostics.StockFrame

这里有一个MSDN示例

我不知道如何在调用堆栈跟踪中获得每个函数的参数值,但一个解决方案是捕获特定的异常(示例中为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

我很欣赏你关于本地化错误字符串的声明,但如果你的听众"大多是程序员",那么惯例已经在某种程度上要求了对英语的理解(正确或错误-但这是另一个争论!)