views:

597

answers:

7

I would like to have some kind of catch-all exceptions mechanism in the root of my code, so when an app terminates unexpectedly I can still provide some useful logging.

Something along the lines of

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

While this all works fine, my problem is when I want to attach the debugger after the exception has been raised.

Since the exception escapes to the runtime, windows will prompt to attach visual studio, except, since it has been rethrown, all locals and parameters further up the stack have been lost.

Is there anyway to log these exceptions, while still providing a way to attach the debugger and retain all useful information?

A: 

Shouldn't simply do a

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

inside the catch do the trick (at least you can examine the outer exception)?

massimogentilini
A: 

If you're strictly using Debug mode for developing and Release mode for deploying, you could try using the System.Diagnostics.Debugger class.

catch (Exception e) {
#if DEBUG
            System.Diagnostics.Debugger.Launch()
#endif
            LogException(e);
            throw;
        }
Dan Goldstein
Unfortunately this:a) doesn't give the *option* to launch the debuggerb) still clears info on the stack, since it catches the exceptionWhat I'm looking for is probably some runtime function to hook in to, not a catch at the root of my program.
PlacidBox
A: 

You can get information of the exception by writing into the trace log:

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

Use DebugView to display the trace log. Stopping the Thread for a few seconds will give you some time to attach your debugger to process without loosing the original exception.

Alexander
+2  A: 

Horribly hacky but without runtime hooks (I know of none) the only way to get to the stack frame where you are throwing from....

All exceptions which are known to be terminal thrown would have to have the following in their constructor:

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

This would present a dialogue allowing the user to either supply the relevant debugger or choose no and no debugging will occur (either way the exception will finish being constructed then be thrown. This obviously only works on exceptions whose source you control.

I don't recommend this, in a debug build with debugger attached you can just choose to get 'First Chance' break on exception throwing which should normally be sufficient to your needs.

Another option is to start generating mini dumps programmatically at the exception throw sites, such data could then be inspected later with a tool like windbg but not interfere too much with the desired behaviour of the exception unwinding the stack after.

The act of the exception reaching your catch-all trap is precisely the stack unwind you don't want, sorry. If you're comfortable with C++ and fancy it you could build a minor (but complex to get right) fork of mono which caused all exceptions to trigger the debugger. Alternatively just rebuild mono's BCL Exception class to do the same as detailed above..

ShuggyCoUk
+1  A: 

Why don't you just let it crash and then register to receive the Windows Error reports from Microsoft? Check out http://msdn.microsoft.com/en-us/isv/bb190483.aspx for the details.

If you don't want to do that, you can use the IsDebuggerPresent function (http://msdn.microsoft.com/en-us/library/ms680345.aspx), and if the result is False, instead of wrapping your code in a try-catch, add an event handler to the AppDomain.UnhandledException event (http://msdn.microsoft.com/en-us/library/system.appdomain.unhandledexception.aspx)

Paul Betts
A: 

If none of the global events work out, then what you could try is putting the error catch in any events your software handles. The ease of these depends on the complexity of your applications. Many of application written with the .NET framework are written to handle events whether it is a traditional application or a web application. By putting the hooks in the events themselves you should be able to preserve the information in the stack you need.

RS Conley
+13  A: 

As Paul Betts already mentioned, you might be better off using the AppDomain.UnhandledException event instead of a try/catch block.

In your UnhandledException event handler you can log/display the exception and then offer the option to debug e.g. show a form with the exception details and buttons to ignore, debug or quit.

If the user selects the debug option, call System.Diagnostics.Debugger.Break() which allows the user to attach whatever debugger they want with the full call stack still available.

Obviously, you could disable this option for any builds you know you're not ever going to be attaching the debugger to.

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();
    }
}
Leaf Garland
@PlacidBox does this answer your question? Why wasn't it given the bounty?
Scott Langham