6

我想在我的代码根目录中有某种包罗万象的异常机制,所以当应用程序意外终止时,我仍然可以提供一些有用的日志记录。

类似的东西

static void Main () {
    if (Debugger.IsAttached)
        RunApp();
    else {
        try {
            RunApp();
        }
        catch (Exception e) {
            LogException(e);
            throw;
        }
    }
 }

虽然这一切都很好,但我的问题是当我想在引发异常后附加调试器时。

由于异常逃逸到运行时,Windows 将提示附加 Visual Studio,但由于它已被重新抛出,因此堆栈上方的所有局部变量和参数都已丢失。

无论如何要记录这些异常,同时仍然提供一种附加调试器并保留所有有用信息的方法?

4

7 回答 7

15

正如 Paul Betts 已经提到的,您最好使用AppDomain.UnhandledException事件而不是 try/catch 块。

在您的 UnhandledException 事件处理程序中,您可以记录/显示异常,然后提供调试选项,例如显示带有异常详细信息和按钮的表单以忽略、调试或退出。

如果用户选择调试选项,调用System.Diagnostics.Debugger.Break()允许用户附加他们想要的任何调试器,而完整的调用堆栈仍然可用。

显然,您可以为您知道永远不会将调试器附加到的任何构建禁用此选项。

class Program
{
    static void Main()
    {
        AppDomain.CurrentDomain.UnhandledException += ExceptionHandler;

        RunApp();
    }

    static void ExceptionHandler(object sender, UnhandledExceptionEventArgs e)
    {
        Console.WriteLine(e.ExceptionObject);
        Console.WriteLine("Do you want to Debug?");
        if (Console.ReadLine().StartsWith("y"))
            Debugger.Break();
    }

    static void RunApp()
    {
        throw new Exception();
    }
}
于 2009-02-13T17:42:10.913 回答
2

骇人听闻,但没有运行时钩子(我不知道),这是到达您要抛出的堆栈帧的唯一方法....

所有已知会被终端抛出的异常都必须在其构造函数中包含以下内容:

#if DEBUG
System.Diagnostics.Debugger.Launch()
#endif

这将显示一个对话框,允许用户提供相关的调试器或选择否,并且不会发生调试(无论哪种方式,异常都将完成构造然后被抛出。这显然仅适用于您控制其源的异常。

我不建议这样做,在带有调试器的调试版本中,您可以选择在异常抛出时获得“第一次机会”中断,这通常应该足以满足您的需求。

另一种选择是在异常抛出站点以编程方式开始生成小型转储,然后可以稍后使用诸如 windbg 之类的工具检查此类数据,但不会过多地干扰之后展开堆栈的异常的所需行为。

异常到达你的捕获所有陷阱的行为正是你不想要的堆栈展开,抱歉。如果您对 C++ 感到满意并且喜欢它,您可以构建一个较小的(但要正确处理)单声道分支,这会导致所有异常都触发调试器。或者,只需重建 mono 的 BCL Exception 类以执行与上述相同的操作。

于 2009-02-12T00:25:36.983 回答
1

为什么不让它崩溃然后注册以接收来自 Microsoft 的 Windows 错误报告?查看http://msdn.microsoft.com/en-us/isv/bb190483.aspx了解详细信息。

如果您不想这样做,您可以使用 IsDebuggerPresent 函数 ( http://msdn.microsoft.com/en-us/library/ms680345.aspx ),如果结果为 False,而不是包装您的代码在 try-catch 中,将事件处理程序添加到 AppDomain.UnhandledException 事件 ( http://msdn.microsoft.com/en-us/library/system.appdomain.unhandledexception.aspx )

于 2009-02-12T03:28:59.023 回答
0

不应该简单地做一个

Exception e1 = e;
LogException(e);
throw(e1);

在 catch 里面做这个把戏(至少你可以检查外部异常)?

于 2008-10-17T10:17:16.347 回答
0

如果您严格使用调试模式进行开发并使用发布模式进行部署,您可以尝试使用 System.Diagnostics.Debugger 类。

catch (Exception e) {
#if DEBUG
            System.Diagnostics.Debugger.Launch()
#endif
            LogException(e);
            throw;
        }
于 2008-10-17T12:00:33.220 回答
0

您可以通过写入跟踪日志来获取异常信息:

        private static void Main(string[] args)
    {
        try
        {
            // ...
        }
        catch (Exception exception)
        {
            System.Diagnostics.Trace.Write(exception);
            #if DEBUG
            System.Diagnostics.Trace.Write("Waiting 20 seconds for debuggers to attach to process.");
            System.Threading.Thread.Sleep(20000);
            System.Diagnostics.Trace.Write("Continue with process...");
            #endif
            throw;
        }
    }

使用DebugView显示跟踪日志。停止线程几秒钟将使您有时间将调试器附加到进程而不会丢失原始异常。

于 2009-02-11T10:14:48.573 回答
0

如果没有一个全局事件起作用,那么您可以尝试将错误捕获放在您的软件处理的任何事件中。这些操作的难易程度取决于您的应用程序的复杂性。许多使用 .NET 框架编写的应用程序都是为了处理事件而编写的,无论它是传统应用程序还是 Web 应用程序。通过将钩子放在事件本身中,您应该能够将信息保留在您需要的堆栈中。

于 2009-02-13T14:12:43.383 回答