This project has moved and is read-only. For the latest updates, please go here.

Logging an exception without showing the Exception Form

Jan 9, 2009 at 3:16 PM
In some cases (I think when catching AppDomain.CurrentDomain.UnhandledException and UnhandledExceptionEventArgs.IsTerminating = true) when displaying the exception dialog, it crashes upon clicking buttons (e.g. "Save") or just when closing the form. So I wonder if there is a way to log the exception and environment details in a file (simulating clicking "Save").

I found a class ExceptionReportPresenter with method BuildExceptionString(), but the class is internal. Any other ideas?
Jan 9, 2009 at 10:00 PM
Edited Jan 10, 2009 at 1:30 AM

There's no reason why the ExceptionReporter should crash on "save" or closing down - so we should look at fixing this instead.

'We need to establish if this is really ExceptionReporter. How does it "crash"?

I know that the ExceptionReporter will throw an (internal) exception if there's no StackTrace in the Exception you pass it.

Jan 12, 2009 at 9:34 AM
Edited Jan 12, 2009 at 11:32 AM

I think that the problem is that the ExceptionReporter form is displayed in an application domain that is unloading. In this case there is only time to log the error; the application cannot recover. Having ExceptionReporter form shown in such application domain causes it to crash or hang. Throwing an exception in a non-GUI thread that is not the main thread and showing the form when handling UnhandledException leads to the following stack trace when clicking "Save" (I used "Managed Stack Explorer" to get it)

        0. [Internal thisFrame, 'M-->U', System.Windows.Forms.FileDialogNative.IFileDialog::Show] (Source Unavailable)
    1. System.Windows.Forms.FileDialog.RunDialogVista (Source Unavailable)
    2. System.Windows.Forms.FileDialog.RunDialog (Source Unavailable)
    3. System.Windows.Forms.CommonDialog.ShowDialog (Source Unavailable)
    4. System.Windows.Forms.CommonDialog.ShowDialog (Source Unavailable)
    5. ExceptionReporting.Views.ExceptionReportView.Save_Click (Source Unavailable)
    6. System.Windows.Forms.Control.OnClick (Source Unavailable)
    7. System.Windows.Forms.Button.OnClick (Source Unavailable)
    8. System.Windows.Forms.Button.OnMouseUp (Source Unavailable)
    9. System.Windows.Forms.Control.WmMouseUp (Source Unavailable)
    10. System.Windows.Forms.Control.WndProc (Source Unavailable)
    11. System.Windows.Forms.ButtonBase.WndProc (Source Unavailable)
    12. System.Windows.Forms.Button.WndProc (Source Unavailable)
    13. System.Windows.Forms.Control.ControlNativeWindow.OnMessage (Source Unavailable)
    14. System.Windows.Forms.Control.ControlNativeWindow.WndProc (Source Unavailable)
    15. System.Windows.Forms.NativeWindow.Callback (Source Unavailable)
    16. [Internal thisFrame, 'M-->U', System.Windows.Forms.UnsafeNativeMethods::DispatchMessageW] (Source Unavailable)
    17. System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop (Source Unavailable)
    18. System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner (Source Unavailable)
    19. System.Windows.Forms.Application.ThreadContext.RunMessageLoop (Source Unavailable)
    20. System.Windows.Forms.Application.RunDialog (Source Unavailable)
    21. System.Windows.Forms.Form.ShowDialog (Source Unavailable)
    22. System.Windows.Forms.Form.ShowDialog (Source Unavailable)
    23. ExceptionReporting.Views.ExceptionReportView.ShowExceptionReport (Source Unavailable)
    24. ExceptionReporting.ExceptionReporter.Show (Source Unavailable)
    25. VSMSIDE.Program.CurrentDomain_UnhandledException (Program.cs:61)
Jan 13, 2009 at 3:31 AM
Edited Jan 13, 2009 at 7:17 AM

Yeah, ExceptionReporter (nor any of your UI) should be accessed on a separate thread
And I don't think it's ExceptionReporter's job to try and cope with parent processes dying or faling while it's trying to display.
You need to provide an environment where ExceptionReporter can show a dialog, that is its job.

The case that you're describing requires you to change where or how you're calling ExceptionReporter.
If you're using BackgroundWorker, or similar, you can easily join to the main thread and show the ExceptiontionReporter on the main thread.
If you're calling ExceptionReporter while you're program or machine is dying or unloading, then I don't expect anything to work.

Jan 13, 2009 at 8:37 AM
I also don't think it is ExceptionReporter's job to handle such cases, but in order to prevent the application from hanging, I have two alternatives when dealing with application terminating exceptions:
  • Don't show the ExceptionReporter form, only log the error in some default log file (how can I get the exception string?)
  • Show the ExceptionReporter form, but don't prevent the user from taking actions that can lead to hanging (i.e. "Save")
Can I accomplish those goals without changing the code of the ExceptionReporter?

Jan 15, 2009 at 1:19 PM
I really think that the problem is getting control of your application.
Whether you need to catch the right event, or catch the right exception, I can't be sure.
You seem to be unsure about the state of your application or why it's unloading.
No solution will help you until you solve that problem and get back in control - once you figure that out, and you're sitting here with an application in a valid state, with a valid exception... then you can call the ExceptionReporter.

Jan 19, 2009 at 8:11 AM
Edited Jan 19, 2009 at 8:16 AM
Well, recovering from all exceptions is not that easy in .NET...

In Windows Forms applications, there are the following kinds of exceptions, that can be handled using different means.

a)Exception in the main GUI thread.

b) Exception in another GUI thread

c) Exception in non-GUI thread

d) Exception in worker (ThreadPool) threads and Timer threads (in .NET 1.1 those were simply ignored by the CLR, but in .NET 2.0 they are like (c) exceptions, unless we use in the app.config file legacyUnhandledExceptionPolicy=”true” )

The CLR provides event handlers that can be use to log and eventually to recover from some exceptions.

By default exception of type (a) can be handled by Application.ThreadException. In this case the error-reporting form works OK. Also by using  Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException) we can also handle (b) type exceptions using the same handler.

By default exception of type (b),(c) and (d) can be caught by AppDomain.CurrentDomain.UnhandledException, but are not recoverable (i.e. they cause the application to stop). In this case if we show the error-reporting form, clicking on “Save” causes the application to hang, which is undesirable.

THat's why I was asking for a way to get the exception details as string, without opening the form. I think that this can be useful in another scenarios, too.

Jan 24, 2009 at 10:41 AM
Edited Jan 24, 2009 at 10:57 AM
Right, I'm with you.
When you said "exception string", you meant the string that I would call the "Exception Report". Although, I've shot myself in the foot by naming the class "ExceptionStringBuilder"

Sorry, the wording and your surrounding issue threw me to what you were really after...

We'd need to make the ExceptionReportPresenter class public and then you could call the Presenter's methods:

string exceptionString = BuildExceptionString();

Which is not ideal, because you'd also have to do some work to get the presenter the ReportInfo settings it needs.

If you were going to do that, it would be quite a hack right now. You would have to edit the source code to make some classes public, so I should confirm - there is currently no way to do what you want without changing ExceptionReporter's code.

But this would be a reasonable feature ("expose the ExceptionStringBuilder as an API) - I might add it to the IssueTracker.

Jan 26, 2009 at 9:28 AM
Yes, such change request sounds good.
Jan 31, 2009 at 3:36 AM
Edited Jan 31, 2009 at 10:39 AM

If you check out the latest source code, I believe you will be able to use the ExceptionReporter in the way that you want.

Basically you will write code like this:

var reportGenerator = new ExceptionReportGenerator(new ExceptionReportInfo() { TheException = yourException } );
var report = reportGenerator.CreateExceptionReport();

See ReportGenerator_Tests.cs for a concrete example.