Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version

Automatic Error Handling

, 17 Mar 2008 CPOL
Handle web and WinForms exceptions automatically.
using System;
using System.Collections.Generic;
using System.Text;

using System.Collections;
using System.Collections.Specialized;
using System.Data;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Net;
using System.Net.Mail;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;

namespace ErrorHandler
{
	public class ExceptionHandler : HandlerBase
	{
		#region Static Entry Points
		public static void AddHandler( bool isConsoleApp )
		{
			_isConsoleApp = isConsoleApp;

			AddHandler( !_isConsoleApp, _isConsoleApp, _isConsoleApp );
		}

		public static void AddHandler( bool screenshot, bool showUser, bool kill )
		{
			_makeScreenshot = screenshot;
			_logToUI = showUser;
			_killAppOnException = kill;

			// attempt to load optional settings from .config file
			//LoadConfigSettings();

			// track the parent assembly that set up error handling
			// need to call this NOW so we set it appropriately; otherwise
			// we may get the wrong assembly at exception time!
			if( Assembly.GetEntryAssembly() == null )
				_parentAssembly = Assembly.GetCallingAssembly();
			else
				_parentAssembly = Assembly.GetEntryAssembly();

			// Attach handlers
			Application.ThreadException += new ThreadExceptionEventHandler( ThreadExceptionHandler );

			AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler( UnhandledException );
		}

		public static void HandleException( Exception ex )
		{
			HandleException( string.Empty, ex );
		}

		public static void HandleException( string msg, Exception exceptn )
		{
			HandleException( msg, exceptn, _logToUI );
		}

		public static void HandleException( string msg, Exception ex, bool showToUI )
		{
			ExceptionHandler handler = new ExceptionHandler();

			handler.HandleExceptionInternal( msg, ex, showToUI );
		}
		#endregion

		#region Entry Point Worker Methods
		private void HandleExceptionInternal( string msg, Exception exceptn, bool showToUI )
		{
			Cursor.Current = System.Windows.Forms.Cursors.WaitCursor;

			// Initialize properties
			_exceptionText = msg;

			if( !string.IsNullOrEmpty( _exceptionText ) )
			{
				_exceptionText += Environment.NewLine;
			}

			_exceptionType = string.Empty;

			_screenshotFullName = string.Empty;
			_logfileFullName = string.Empty;

			_logToScreenshotOK = false;
			_logToDatabaseOK = false;
			_logToFileOK = false;
			_logToEventLogOK = false;

			// turn the exception into an informative string
			try
			{
				_exceptionText += ExceptionToString( exceptn, true );
				_exceptionType = exceptn.GetType().FullName;
			}
			catch( Exception ex )
			{
				_exceptionText += "Error while generating exception string: " + ex.ToString();
				_exceptionType = ex.GetType().FullName;
			}

			if( _makeScreenshot )
			{
				try
				{
					TakeScreenshot();
					_exceptionText += Environment.NewLine + "Screenshot placed at:	" + _screenshotFullName;
				}
				catch( Exception ex )
				{
					_exceptionText += Environment.NewLine + "Screenshot Error:" + ExceptionToString( ex, true );
				}
			}

			Write( AppDomain.CurrentDomain.FriendlyName, "Application Error: ", new string[] { _screenshotFullName }, new Dictionary<string,Stream>() );

			Cursor.Current = System.Windows.Forms.Cursors.Default;

			// display message to the user
			if( showToUI )
			{
				try
				{
					ExceptionToUI();
				}
				catch
				{
					// Already logged other problems, can't attempt to log this for the risk of a closed loop
				}
			}
		}

		private static void ThreadExceptionHandler( object sender, System.Threading.ThreadExceptionEventArgs args )
		{
			HandleException( args.Exception );

			if( _killAppOnException )
			{
				Application.Exit();
			}
		}

		private static void UnhandledException( object sender, UnhandledExceptionEventArgs e )
		{
			HandleException( (Exception)e.ExceptionObject );

			if( _killAppOnException )
			{
				Application.Exit();
			}
		}
		#endregion

		#region Config Fields
		private static bool _makeScreenshot = false;
		private static bool _logToUI = false;
		private static bool _killAppOnException = false;
		private static bool _isConsoleApp = false;

		private static string _screenshotFileName = "ErrorScreen";
		private static ImageFormat _screenshotImageFormat = ImageFormat.Png;
		#endregion

		#region Exception Processing State Fields

		private string _screenshotFullName = string.Empty;
		private string _logfileFullName = string.Empty;

		private bool _logToScreenshotOK = false;
		#endregion

		#region Inputs
		#region Win32api screenshot calls
		// Windows API calls necessary to support screen capture
		[DllImport( "gdi32.dll" )]
		private static extern int BitBlt( int hDestDC, int x, int y, int nWidth, int nHeight, int hSrcDC, int xSrc, int ySrc, int dwRop );

		[DllImport( "user32.dll" )]
		private static extern int GetDC( int hwnd );

		[DllImport( "user32.dll" )]
		private static extern int ReleaseDC( int hwnd, int hdc );
		#endregion

		private void TakeScreenshot()
		{
			// note that screenshotname does not include the file type extension
			try
			{
				Rectangle screenRect = Screen.PrimaryScreen.Bounds;
				Bitmap screenBitmap = new Bitmap( screenRect.Width, screenRect.Height );
				Graphics screenGraphics = Graphics.FromImage( screenBitmap );

				// get a device context to the windows desktop and our destination  bitmaps
				int hdcSrc = GetDC( 0 );
				IntPtr hdcDest = screenGraphics.GetHdc();

				// copy what is on the desktop to the bitmap
				const int SRCCOPY = 0xCC0020;
				BitBlt( hdcDest.ToInt32(), 0, 0, screenRect.Width, screenRect.Height, hdcSrc, 0, 0, SRCCOPY );

				// release device contexts
				screenGraphics.ReleaseHdc( hdcDest );
				ReleaseDC( 0, hdcSrc );

				string extension = "." + _screenshotImageFormat.ToString().ToLower();

				// Find a name that isn't used (decorated by time)
				do
				{
					_screenshotFullName = Path.Combine( ApplicationPath, string.Format( "{0}[{1}]", _screenshotFileName, DateTime.Now.Ticks ) );

					if( Path.GetExtension( _screenshotFullName ) != extension )
						_screenshotFullName += extension;

				} while( File.Exists( _screenshotFullName ) );

				//switch( strFormatExtension )
				//{
				//case "jpeg":
				//   BitmapToJPEG( screenBitmap, strFilename, 80 );
				//   break;

				//default:
				screenBitmap.Save( _screenshotFullName, _screenshotImageFormat );
				//   break;
				//}

				_logToScreenshotOK = true;
			}
			catch( Exception )
			{
				_logToScreenshotOK = false;
			}
		}
		#endregion

		#region Outputs
		private void ExceptionToUI()
		{
			string msg = string.Format( "A {0} error occurred.  Your last action probably failed to finish.", _exceptionType );

			if( _logToScreenshotOK )
				msg += Environment.NewLine + Environment.NewLine + "A screen shot of the error condition was made.";

			if( _logToDatabaseOK || _logToEventLogOK || _logToFileOK )
				msg += Environment.NewLine + Environment.NewLine + "The error has been logged.";

			if( _killAppOnException )
				msg += Environment.NewLine + Environment.NewLine + "This application might exit.  If it does, you can restart it and try your task again.";

			if( _isConsoleApp )
			{
				Console.WriteLine( msg );
			}
			else
			{
				MessageBox.Show( msg, "Application Error" );
			}
		}
		#endregion

		#region String Conversions

		/// <summary>
		/// gather some system information that is helpful to diagnosing exception
		/// </summary>
		/// <returns></returns>
		protected override string SysInfoToString()
		{
			return SysInfoToString( false );
		}

		/// <summary>
		/// gather some system information that is helpful to diagnosing exception
		/// </summary>
		/// <param name="blnIncludeStackTrace"></param>
		/// <returns></returns>
		private string SysInfoToString( bool blnIncludeStackTrace )
		{
			StringBuilder builder = new StringBuilder();

			builder.Append( "Date and Time:         " );
			builder.Append( DateTime.Now );
			builder.Append( Environment.NewLine );

			builder.Append( "Machine Name:          " );
			try
			{
				builder.Append( Environment.MachineName );
			}
			catch( Exception e )
			{
				builder.Append( e.Message );
			}
			builder.Append( Environment.NewLine );

			builder.Append( "IP Address:            " );
			builder.Append( GetCurrentIP() );
			builder.Append( Environment.NewLine );

			builder.Append( "Current User:          " );
			builder.Append( ProcessIdentity() );
			builder.Append( Environment.NewLine );
			builder.Append( Environment.NewLine );

			builder.Append( "Application Domain:    " );
			try
			{
				builder.Append( System.AppDomain.CurrentDomain.FriendlyName );
			}
			catch( Exception e )
			{
				builder.Append( e.Message );
			}


			builder.Append( Environment.NewLine );
			builder.Append( "Assembly Codebase:     " );
			try
			{
				builder.Append( _parentAssembly.CodeBase );
			}
			catch( Exception e )
			{
				builder.Append( e.Message );
			}
			builder.Append( Environment.NewLine );

			builder.Append( "Assembly Full Name:    " );
			try
			{
				builder.Append( _parentAssembly.FullName );
			}
			catch( Exception e )
			{
				builder.Append( e.Message );
			}
			builder.Append( Environment.NewLine );

			builder.Append( "Assembly Version:      " );
			try
			{
				builder.Append( _parentAssembly.GetName().Version.ToString() );
			}
			catch( Exception e )
			{
				builder.Append( e.Message );
			}
			builder.Append( Environment.NewLine );

			builder.Append( "Assembly Build Date:   " );
			try
			{
				builder.Append( GetAssemblyBuildDate( _parentAssembly, false ).ToString() );
			}
			catch( Exception e )
			{
				builder.Append( e.Message );
			}
			builder.Append( Environment.NewLine );
			builder.Append( Environment.NewLine );

			if( blnIncludeStackTrace )
				builder.Append( EnhancedStackTrace() );

			return builder.ToString();
		}


		/// <summary>
		/// get IP address of this machine
		/// </summary>
		/// <remarks>Not an ideal method for a number of reasons (guess why!) but the alternatives are very ugly</remarks>
		/// <returns></returns>
		private static string GetCurrentIP()
		{
			try
			{
				return System.Net.Dns.GetHostEntry( System.Net.Dns.GetHostName() ).AddressList[0].ToString();
			}
			catch( Exception )
			{
				return "127.0.0.1";
			}
		}
		#endregion
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Andy Searls
Software Developer (Senior) Belami
United States United States
No Biography provided

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.150128.1 | Last Updated 17 Mar 2008
Article Copyright 2008 by Andy Searls
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid