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
}
}