In .NET, the default exception handler will let the user continue running the program. However, I'd like to have a global exception handler that saves the stack trace to an "errorlog.txt" file so the user can send it to me and doesn't have to remember to click "Details" and copy it out of the dialog (and remove all the useless crap about loaded assemblies and such). But when I do this, the code doesn't know how to continue, so all I can do is exit the app. Is there any way to have the best of both worlds? (Yes, I know what I'm asking for is essentially "On Error Resume Next" with logging, but I really think it would be useful!)
-
7What kind of application are you working with? WinForms, WPF, ASP.NET, etc.? – Justin Niessner Sep 24 '13 at 14:32
-
2http://msdn.microsoft.com/en-us/library/system.windows.forms.application.threadexception.aspx. – SLaks Sep 24 '13 at 14:32
-
3How would your your app continue running after a thread abort or access violation exception? – asawyer Sep 24 '13 at 14:33
-
3@asawyer Or, maybe a Stack Overflow? ;) – Kendall Frey Sep 24 '13 at 14:34
-
possible duplicate of [Is it possible to continue running code from the point of failure?](http://stackoverflow.com/questions/18595060/is-it-possible-to-continue-running-code-from-the-point-of-failure) – Daniel A. White Sep 24 '13 at 14:35
-
It's a WinForms app. If there is a critical error that can't be caught, it might as well crash, but I'd like to at least catch the ones that I can. – ekolis Sep 25 '13 at 17:45
8 Answers
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
If you bind yourself to this event when the application starts, you should be able to catch any Unhandled Exception your application throws, and save it to a file. (Use the Exception
object part of the UnhandledExceptionEventArgs
. I do not believe it is possible to resume from where the error Occurred.
-
Thanks, but I've already got what amounts to a try/catch in the Main method, which does the same thing as what you suggested, if I'm not mistaken... – ekolis Sep 25 '13 at 17:50
-
This is NOT what OP asked for... "global exception handler" not "global unhandled exception handler"... – Yousha Aleayoub Dec 02 '19 at 13:02
You can write a global exception handler method that you call in every catch
block, which writes the stack trace where ever you want to save it. But you'd need to write try . . . catch
blocks for every operation that needs them and call the exception handler in each.
You can also call that global exception handler method in the MyApplication.UnhandledException
handler for all unhandled events. But when control gets to that method in that case, the program is not going to continue running.

- 8,298
- 15
- 67
- 123
-
Too bad C# doesn't have checked exceptions so I can't miss any... that feature is actually somewhat useful in Java, if annoying at times! – ekolis Sep 25 '13 at 17:51
-
1Just to note, you can continue execution by setting "Handled" to true on the event arg. Your app may be in a very weird state, of course, but you could in theory recover. – Egor Apr 10 '14 at 23:02
In a Winform app you can attach an handler to the Application.ThreadException
event (make sure you do before calling Application.Run()
). This will get rid of the standard exception dialog (the one with the "details" button) and give you the power to display / log anything you want.
However, remember that this will work ONLY for exceptions thrown within the UI thread. Exceptions raised from a background thread won't be reachable from your handler. These can still be caught by AppDomain.UnhandledException
handler, though.
-
1Some advice, now: even though this IMHO does answer your question, I don't recommend that you do that. You really should make sure your app will end on an unhandled exception, by using Application.SetUnhandledExceptionMode method with ThrowException mode. This will disable Application.ThreadException event and the default exception dialog with details, but it will STILL allow AppDomain.UnhandledException to be raised. From there you can display something to your user (watch out for threading issues) and log the exception right before the app ends. This is the way to go, again IMHO. – Crono Sep 25 '13 at 13:49
in main constructor, put this and will do it globally:
AppDomain.CurrentDomain.FirstChanceException += (sender, eventArgs) =>
{
MessageBox.Show(eventArgs.Exception.ToString());
};
(Note: as said in comments, this might not be thread-safe).

- 53,146
- 19
- 236
- 237
-
This is probably the best answer, except that it doesn't give you a stack trace. – GoldenretriverYT Sep 24 '22 at 09:06
-
1Expect issues with this in a multithreaded application. Only one thread can be used for UI. – Crono Jan 17 '23 at 13:30
No that does not exist, exceptions are a flow control construct, so On Error Resume Next
is not possible.
You could do your operation in a loop and on an exception, retry your logic.
KandallFrey is right however, you shouldn't use exceptions as flow control, use them only in exceptional cases.

- 187,200
- 47
- 362
- 445
-
3
-
1@KendallFrey i'm not saying that they can be used as flow control, but what they effectively are. – Daniel A. White Sep 24 '13 at 14:34
-
I'm not using them for flow control. I am using them to handle exceptional cases, but I do want the app to be able to recover gracefully, while still logging the error. – ekolis Sep 25 '13 at 17:46
I use My.Application.UnhandledException
to open a dialog form where the user can save every information about the exception.
When the form is closed I call e.ExitApplication = False
.

- 10,858
- 1
- 25
- 48
-
1I think this is for Visual Basic assemblies only (http://msdn.microsoft.com/en-us/library/3a02k5s0%28v=vs.90%29.aspx). – Surfbutler Sep 24 '13 at 14:57
-
Yes, you're right. For C# users I found this: http://msdn.microsoft.com/it-it/library/microsoft.visualbasic.applicationservices.windowsformsapplicationbase.unhandledexception.aspx – tezzo Sep 24 '13 at 15:12
At my firm, I've built an exception handling framework which has served me well for 7 years now. Every assembly references the DLL, and every method in each assembly has a try-catch block. In the catch, I basically have to make one decision based on the question "where do I want my exception handling framework to intervene, i.e. to log the exception data externally and inform the user of the problem?". In most cases, the answer to this question is that I want it to intervene in cases where the method is called externally, e.g. if it's inside an event handler or other delegate. So here is some example code:
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Try
Method1()
Catch ex As Exception
BSExceptionHandler.ProcessException(BSExceptionHandler.GetBSException(ex, BSExceptionHandler.GetThisMethodName))
End Try
End Sub
Private Sub Method1()
Try
method2()
Catch ex As Exception
Throw BSExceptionHandler.GetBSException(ex, BSExceptionHandler.GetThisMethodName)
End Try
End Sub
Private Sub method2()
Try
Dim x As Integer = CInt("x") 'exception thrown here.
Catch ex As Exception
Throw BSExceptionHandler.GetBSException(ex, BSExceptionHandler.GetThisMethodName)
End Try
End Sub
When Throw BSExceptionHandler.GetBSException(ex, BSExceptionHandler.GetThisMethodName)
is called for the first time in Method2()
, the original exception is wrapped inside a BSException
, and the original method name is stored as well (more on that later). Any subsequent calls to BSExceptionHandler.GetBSException(ex, BSExceptionHandler.GetThisMethodName)
, e.g. in Method1()
, will recognize that the exception is already of type BSException
and just throw that instead of re-wrapping it. Ultimately, it reaches the "top" of the call stack (not really the top, just the point at which I've decided I want to process the exception through my framework -- i.e. log it, inform the user, etc.) -- in this case, in Button1_Click
-- and then BSExceptionHandler.ProcessException(BSExceptionHandler.GetBSException(ex, BSExceptionHandler.GetThisMethodName))
is called.
What my framework does in ProcessException
is to determine the type of reporting configured on the system. This is defaulted to "simple", which means the user gets a very general and minimally-intrusive dialog box indicating a problem has occurred and instructing them to contact the IT department. However, there is an optional registry setting which will set the reporting type to either "verbose" or to "email". I use "verbose" on my development machine, because it includes all the exception info in the dialog box so I don't have to go look at the log. The "email" option is used on servers where I have applications running when there is no user logged on; in that case, the error information is emailed to the IT department to alert them of the issue.
The other thing that happens from here, regardless of the reporting type, is that the error information is recorded to the Windows event log. Here is an example:
SILENT: No
ROOT ASSEMBLY: C:\Users\roryap\AppData\Local\Temporary Projects\WindowsApplication1\bin\Debug\WindowsApplication1.exe
DESCRIPTION: BromsunExceptionHandling.BSException: Calling Method 'WindowsApplication1.Form1.method2()' produced 'System.FormatException' exception with message 'Conversion from string "x" to type 'Integer' is not valid.'.
CALLING METHOD: WindowsApplication1.Form1.method2()
STACK TRACE: at Microsoft.VisualBasic.CompilerServices.Conversions.ToInteger(String Value)
at WindowsApplication1.Form1.method2() in C:\Users\roryap\AppData\Local\Temporary Projects\WindowsApplication1\Form1.vb:line 32
CALL STACK: WindowsApplication1.Form1.method2()
WindowsApplication1.Form1.Method1()
WindowsApplication1.Form1.Button1_Click(sender As System.Object, e As System.EventArgs)
SOURCE: Microsoft.VisualBasic
TARGET SITE: Int32 ToInteger(System.String)
EXTRA INFO:
When the user reports the issue to IT, all IT has to do is check their event log to get the error info. Furthermore, the application does not exit; it continues running because it never allows the exception to bubble up outside of the point where you've chosen to process the exception with the handling framework. Once it's handled by the framework, it's done.
While there are a few downsides to this approach, I have found it to be an extremely robust system and it has saved me many many hours of blind troubleshooting over the years. I have created snippets so that all I have to do is create a try-catch block then type the snippet shortcut -- e.g. "pbs" or "tbs" -- into the catch-block and hit the tab key to populate the appropriate exception handling method. Therefore, it's quite painless to use this framework in every method. I've even modified my VS project templates to always reference and include the framework so I don't have to do that for every new project.
So, regarding the GetThisMethodName()
function: this method uses System.Diagnostics.StackTrace
, System.Diagnostics.StackFrame
, and System.Reflection.MethodBase
to figure out the name of the method where the original exception was wrapped inside the BSException.

- 34,009
- 10
- 83
- 174
-
4
-
2I would not recommend this approach. Only catch exceptions that you are actually going to handle, ie respond to with some logic action, eg retry the save to database or attempt to open the locked file again. If all you are doing is logging the exception then have a global exception handler and log it there (maybe use log4net). You may even make a decision to start handling some of these previously unhandled exceptions – SleepyBoBos Jan 17 '14 at 06:09
-
@SleepyBoBos -- You said, "Only catch exceptions that you are actually going to handle": do you have anything substantive that can back up this claim? – rory.ap Jan 17 '14 at 13:49
-
@SleepyBoBos -- Also, the accepted answer is basically suggesting the same approach, just with a lot less detail. – rory.ap Jan 17 '14 at 13:51
-
SleepyBoBos is absolutely right. Catching every possible `Exception` regardless of its nature means that object instances may remain in a corrupted state, potentially causing others, MUCH harder errors to debug and - worst of all - data loss caused on inaccurate check conditions (caused by state corruption). – Crono Sep 25 '18 at 15:25
-
Backing up the claim is [this article](https://msdn.microsoft.com/en-us/magazine/mt620018.aspx?f=255&MSPPError=-2147217396) from Visual Studio Magazine. – Crono Sep 25 '18 at 15:26
-
Some pointers from the aforementioned article: *AVOID catching exceptions that you’re unable to handle fully.* (...) *Rarely use System.Exception and general catch blocks—except to log the exception before shutting down the application.* – Crono Sep 25 '18 at 15:28
-
I no longer use this approach. What I do now is similar to Samuel's answer here. https://stackoverflow.com/a/18984584/2704659 – rory.ap Sep 25 '18 at 15:47
For me works this solution:
- In WebApiConfig.cs add:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Services.Replace(typeof(IExceptionLogger), new UnhandledExceptionLogger());
...
}
- Create new file UnhandledExceptionLogger.cs:
public class UnhandledExceptionLogger : ExceptionLogger
{
public override void Log(ExceptionLoggerContext context)
{
File.AppendAllText("log.txt", context.Exception.ToString());
}
}

- 39
- 5