views:

72

answers:

2

So the other day my C# application crashed. Usually, with a .NET application, if you have an unhandled exception you get a nice error message with a stack trace.

However, this time, I got a different dialog that just told me there was an error and offered to attach a Debugger, but there was no stack trace in the dialog and the machine it was running on had no debugger installed.

What gives? Why don't I see the default .NET exception handler?

A: 

You probably crashed in unmanaged code that is in use either directly or indirectly by your application.

And if you access protected memory in unmanaged code, that's it. You're not going to manage the exception and bubble/propagate anything. Your process will crash the old fashioned way and you'll need old-fashioned tools and techniques to find the error.

Paul Sasik
I thought unmanaged exceptions bubbled up through the CLR?
ajs410
+5  A: 

Many, many things can cause this to happen:

  • Crashes in unmanaged code (i.e. access violations, bad GDI calls, closed handles, etc.);
  • Unhandled exceptions on background threads;
  • Unhandled exceptions in certain message handlers (i.e. OnPaint);
  • Fatal exceptions such as OutOfMemoryException, StackOverflowException, BadImageFormatException, and so on - which could prevent the global exception handler from ever running;
  • Unhandled exceptions occurring during app initialization or shutdown;
  • Hardware failures - bad memory/disk sectors, etc.
  • ...and many more.

Really the only way to be sure is to determine steps that will reproduce the error and use a tool like WinDbg to actually debug the part that's crashing.

Aaronaught
+1 for being very informative.
Jim Schubert
Of the things you list, the following may apply. 1) I have a background worker who is calling into an unmanaged DLL. I forced an exception in the unmanaged DLL. The Output windows acknowledges the unmanaged first-chance exception, and the subsequent managed first-chance SEHException that wraps it. So the exception bubbles up, but then something swallows it. Who? Why? 2) I have seen EngineExecutionException before so I'm a little familiar with fatal exceptions that the CLR can't catch. It could be this, too.
ajs410
@ajs410: Just because you see the first-chance exception when you're running in the debugger doesn't mean that they "bubble up" so to speak - your app *may* be notified if you subscribe to `AppDomain.UnhandledException` but it will always terminate the runtime regardless and very often will look like a "crash."
Aaronaught
@Aaronaught: Those first chance exceptions in my test were actually swallowed, as in they did not terminate the runtime...they just silently disappeared. Regardless, terminating the runtime is acceptable if I can at least get a stack trace, so I'll look into subscribing to AppDomain.UnhandledException.
ajs410
@ajs410: The .NET 1.1 Framework swallowed background thread exceptions - current versions will not unless you turn on the compatibility option (**not recommended**). If you're getting a true crash, it means that your exception *hasn't* been swallowed - it's crashed the runtime. If the runtime crashes, it's probably too late for you to catch the exception. Of course it doesn't hurt to try anyway, and it will work *sometimes* - but it's really more effective to code defensively inside multi-threaded branches.
Aaronaught
So for my test, I threw an EngineExecutionException from inside my thread. The Exception exists in _RunWorkerCompleted as the Error property of the RunWorkerCompletedEventArgs argument, but there is no big explosion that brings down the app (whoever sets the Error property probably swallowed the exception...). I added a try-catch block to the _DoWork which dumps the stack trace and re-throws, so I can get the stack trace now, but the runtime isn't crashing in my demo so I'm not sure this will notify me of the original error (though it might do so on the way to crashing the runtime...)
ajs410
@ajs410: Throwing an `ExecutionEngineException` is not the same as actually having one happen. You can easily throw and catch an `OutOfMemoryException` too, but that doesn't mean that the same `catch` will successfully run if you're *really* out of memory. FYI, `BackgroundWorker` tasks are a special case of multi-threaded code; the `BackgroundWorker` automatically wraps your work in a `try-catch` block and marshals the exception back to the main thread; on the other hand, if you use a `Thread` or `ThreadPool`, the results will be very different.
Aaronaught
I switched to infinite recursion to bring down the app, and now I'm not getting my stack trace anymore. I tried subscribing to AppDomain.CurrentDomain.UnhandledException in hopes that it would get called along the way to shutting down the process, but the recursive death function appears to bypass the UnhandledExceptionHandler.There really is no option other than to attach a debugger to the process at the moment of the crash, huh?
ajs410
@ajs410: Yep, that's what I said originally. ;) Not all exceptions are recoverable; if the process is mangled enough, exception handlers may never execute (and this includes the default handler).
Aaronaught
I don't mind the process coming down, because I'm not interested in recovery; all I want is the stack trace.
ajs410
@ajs410: I think the point you're missing is that the stack trace is part of the default exception handler, which doesn't run at all if the process has irrecoverably crashed. The only guaranteed way to get a stack trace is to debug the process.
Aaronaught
So...the StackTrace property of the Exception object is generated when the catch handler is invoked? Then, for fatal exceptions no catch handler is ever invoked so the StackTrace property is never populated. BTW, thanks for this discussion, it has been very illuminating.
ajs410
@ajs410: I believe the stack trace is generated when you `throw`, which is why rethrowing an existing exception loses the stack trace. It's not a question of whether or not the `StackTrace` property is *populated*, it's a question of whether or not the managed exception is ever *handled* - if it isn't, it'll crash the runtime. The "stack trace" you're used to seeing dumped out to the console or displayed in a message box is just the default exception handler. Don't confuse the trace itself with the code that presents it to the end user.
Aaronaught
So the stack trace does exist, I just need some way to get it out of the app as it's crashing. I don't need to handle or recover from the exception, all I want is to log the stack trace for review. I was hoping to have the app do it so I don't have to install a debugger on the user's machine. However, it looks like .NET SDK has a Mdbg console-debugger. I can run this from a batch file, throw the output to a log, and tell my users to run the batch file.
ajs410
@ajs410: I guess I'm not explaining this clearly. The `StackTrace` is a managed CLR object. You retrieve it from `Exception.StackTrace`. This is what the default exception handler looks at and displays. If an exception escapes your .NET app, and is not handled by any exception handler, then *your app has gone to Code Heaven*. It can't do anything anymore, it can't display its own stack trace. The only way to get a stack trace is to use an external debugging tool to freeze the program at crash time and inspect its *actual* stack. I believe Mdbg should work for you here.
Aaronaught