Unhandled runtime exceptions window forms (C#)

Introduction

 A unhandled runtime exception in a desktop application occurs when a developer does not anticipate and handle a possible runtime exception. Learn how to handle runtime exceptions that were not anticipated by writing details for an unhandled exception to a comma delimited text file along with presenting a window/form to inform an application needs to close because of a runtime exception.

Microsoft documentation explains how to handle unhandled runtime exceptions along with a basic code sample. Rather than use Microsoft model which has code dealing with unhandled exceptions in each application the code will be in a class project which when referenced by an application requires subscribing to three events.

Log viewer

Example log file
https://github.com/karenpayneoregon/csharp-features/blob/master/ThreadExceptionWindowsFormsApp/bin/Debug/net5.0-windows/ApplicationLog.csv

Prerequisite

  • Microsoft Visual Studio 2019
  • .NET Framework Core 5
  • C# 9

Implementation

First ensure the latest release of Git is installed before continuing.

  • Create a temporary folder. 
  • Create a batch file in the temporary folder
  • Insert the commands which follow. 
  • Run the batch file
  • Copy the two projects to a Visual Studio solution
  • Build (a NuGet restore packages may be needed)
  • Run the application from Windows Explorer as unhandled exception handling can not run under the debugger.

Important

Note

Make sure to read the direction for downloading the source code as the method is different than conventional cloning a GitHub repository.

mkdir code
cd code
git init
git remote add -f origin https://github.com/karenpayneoregon/csharp-features
git sparse-checkout init --cone
git sparse-checkout add ExceptionHandling
git sparse-checkout add ThreadExceptionWindowsFormsApp
git pull origin master
:clean-up
del .gitattributes
del .gitignore
del .yml
del .editorconfig
del *.md
del *.sln

Important notes

  • The form AppErrorForm can be optionally copied to your project (once done change the namespace to your project's namespace) or use your own form to display that the application must close.
  • For viewing error log, copy the form LogViewerForm to your project and change the namespace to your project's namespace

Once finished running the code sample add a reference to ExceptionHandling class project then open Program.cs for another application and paste in the following remembering to change the namespace to the current project namespace and set the startup form. 

Program.cs

01.using System;
02.using System.Windows.Forms;
03.using ExceptionHandling;
04. 
05./*
06. * TODO Change this namespace to your project namespace
07. */
08.namespace ThreadExceptionWindowsFormsApp
09.{
10.    /// <summary>
11.    /// Rig for unhandled exceptions.
12.    /// Note Text property set on error form is for demo purposes only
13.    /// </summary>
14.    static class  Program
15.    {
16. 
17.        [STAThread]
18.        static void  Main()
19.        {
20.             
21.            Application.SetHighDpiMode(HighDpiMode.SystemAware);
22.            Application.EnableVisualStyles();
23.            Application.SetCompatibleTextRenderingDefault(false);
24. 
25.            // Handling UI thread exceptions to the event.
26.            Application.ThreadException += UnhandledExceptions.Application_ThreadException;
27. 
28.            // For handling non-UI thread exceptions to the event. 
29.            AppDomain.CurrentDomain.UnhandledException +=
30.                UnhandledExceptions.CurrentDomain_UnhandledException;
31. 
32.            // Indicates capturing exception has completed
33.            UnhandledExceptions.OnProcessingCompletedEvent += OnProcessingCompletedEvent;
34. 
35.            // TODO Change this to your startup form
36.            Application.Run(new Form1());
37.        }
38.        /// <summary>
39.        /// Display window informing application most close
40.        /// </summary>
41.        private static  void OnProcessingCompletedEvent()
42.        {
43.            var f = new  AppErrorForm { Text = @"Your title goes here" };
44.            f.ShowDialog();
45.            Application.Exit();
46.        }
47.    }
48.}

Exceptionhandling project

The following is not needed to use above code, instead this information points to where operations happen in the event customization is desired.

  • LogManager class is responsible for creating and appending exception details to a comma delimited file using LogLevel enumeration to specify if the exception is of a specific type.
  • if there is a failure to write to the log file a second attempt is made using Exceptions.Write method and if this fails nothing is written to disk.
  • LogReader class provides methods to read the log file along with events to provide instant access to information being read as read from the log file although the method Read can be executes and return a List<LogContainer> to use after all information has been read as done in the suppled code sample.

Summary

All application should have unhandled exception wired in as no developer can boast their application will never have a bug or unexpected issue they didn't count on. By using the code provided this can assist with figuring out issues. For customers running an application which terminated unexpectedly have them email the log as an attachment.

What about using email and/or writing to Windows event logs? Email can fail for many reasons and using Windows event logs can fail if there is a permission issue preventing users from adding to the event logs. Generally writing to a log file in the same folder as the executable is the best option. 

NuGet package

There is a NuGet package which once installed simply add the code mentioned above from Program.cs to your Program.cs file

Install-Package KP.ExceptionHandling -Version 1.0.0

See also

Exception Bubbling in C#
Hiding exception paths
C#: Exception handling in hierarchy 
Working with TraceListener

External

Source code

Is contained in the following repository which has more projects than needed so use the batch file mentioned above to clone the two projects needed.