Exception Handling the nice way

17 09 2008

I’ve knocked up a simple project that demonstrates a few things, namely:

  1. Avoiding file locking issues when writing to a logfile using recursion
  2. Dealing with Unhandled Exceptions globally
  3. Writing this data to a local log file

The source code is below, feel free to download and use as you wish. If you make any improvements, please let me know. Credits to Mark bonafe for his original blog post Mark Bonafe’s Blog

ExceptionHandler.cs

using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Data.SqlClient;
using System.Windows;
using System;

namespace WindowsFormsApplication1
{

///
/// Handles displaying error messages
///
public class ExceptionHandler
{
///
/// Takes the exception message and displays a meaningful message to the user
///
public static string DisplayMessage(Exception ex)
{

return DisplayMessage(ex, “”);
}

///
/// Takes the exception message and displays a meaningful message to the user
///
/// The exception to display.
/// Current User
//[System.Diagnostics.DebuggerStepThrough()]
public static string DisplayMessage(Exception ex, string userName)
{

StringBuilder sb = new StringBuilder();

if (ex is DBConcurrencyException)
sb.Append(
“Concurrency Error: One or more people have updated this data since your last request.”);
else if (ex is SqlException)
{
sb.Append(
“Database Error: “);
switch (((SqlException)ex).Number)
{

case 547:
sb.Append(
“There is a constraint on the items you tried to modify. Please try again.”);
break;
case 2601:
// Unique Index
sb.Append(“Cannot insert duplicate values into the database.”);
break;
case 2627:
// Unique Constraint
sb.Append(“Cannot insert duplicate values into the database.”);
break;
default:
sb.Append(ex.Message);

break;
}
}

else
{
sb.Append(
“Exception Handler Unexpected Error: “ + ex.Message);
}

// Show Developers extra information about the error
// Line numbers, StackTrace, etc.
{
{

string nl = “\n\n”;

sb.Append(nl + “Exception Information:” + nl);
sb.Append(
“Message: “ + ex.Message + nl);
sb.Append(
“Source: “ + ex.Source + nl);
sb.Append(
“Stack Trace: “ + ex.StackTrace + nl);

if (ex.InnerException != null)
{
sb.Append(nl +
“Inner Exception Info:” + nl);
sb.Append(
“Message: “ + ex.InnerException.Message + nl);
sb.Append(
“Source: “ + ex.InnerException.Source + nl);
sb.Append(
“Stack Trace: “ + ex.InnerException.StackTrace + nl);
}
}
}

return sb.ToString();
}
}
}

TestExceptionHandler.cs (form)

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Threading;

namespace WindowsFormsApplication1
{

public class Form1 : Form
{
private LinkLabel linkLabel1;
private Button button1;

public Form1()
{
InitializeComponent();
}
[
STAThread]
static void Main()
{

Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);
Application.Run(new Form1());
}

private static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
{
WriteExceptionToLogFile(
ExceptionHandler.DisplayMessage(e.Exception));
MessageBox.Show(ExceptionHandler.DisplayMessage(e.Exception));
}

private void InitializeComponent()
{

this.button1 = new System.Windows.Forms.Button();
this.linkLabel1 = new System.Windows.Forms.LinkLabel();
this.SuspendLayout();
//
// button1
//
this.button1.Location = new System.Drawing.Point(100, 12);
this.button1.Name = “button1”;
this.button1.Size = new System.Drawing.Size(75, 40);
this.button1.TabIndex = 1;
this.button1.Text = “Throw Exception”;
this.button1.UseVisualStyleBackColor = true;
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// linkLabel1
//
this.linkLabel1.AutoSize = true;
this.linkLabel1.Location = new System.Drawing.Point(60, 55);
this.linkLabel1.Name = “linkLabel1”;
this.linkLabel1.Size = new System.Drawing.Size(165, 13);
this.linkLabel1.TabIndex = 2;
this.linkLabel1.TabStop = true;
this.linkLabel1.Text = https://simonsteed.wordpress.com”;
this.linkLabel1.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel1_LinkClicked);
//
// Form1
//
this.ClientSize = new System.Drawing.Size(292, 75);
this.Controls.Add(this.linkLabel1);
this.Controls.Add(this.button1);
this.Name = “Form1”;
this.ResumeLayout(false);
this.PerformLayout();

}

///
/// Uses recursion to check if the passed in filename is already locked by another process
/// it will continue to check to see if lock is released so that the caller can access it
/// It will currently only ever return true if the file is unlocked but never return false.
/// it would be dead easy to add a timer to this to automatically time out after a set period
/// unless you are happy for your app to wait until the process is finished.
///
/// filename to check
/// true or false
private static bool IsFileUsedbyAnotherProcess(string filename)
{

FileStream fs = null;
try
{
fs =
File.Open(filename, FileMode.OpenOrCreate, FileAccess.Read, FileShare.None);
}

catch (System.IO.IOException exp)
{

string msg = exp.Message;
fs.Close();
fs.Dispose();
IsFileUsedbyAnotherProcess(filename);
// recheck access
}
fs.Close();
fs.Dispose();

return false;
}

private static void WriteExceptionToLogFile(string msg)
{

string LogFile = “log.txt”;
TextWriter tw = null;
try
{
if (!IsFileUsedbyAnotherProcess(LogFile))
{

// create a writer and open the file
tw = new StreamWriter(LogFile, true);

// write a line of text to the file
tw.WriteLine(DateTime.Now + “: ‘” + Environment.MachineName + “‘ – “ + msg);
}
}

catch { }
finally
{
// close the stream
if (tw != null)
{
tw.Close();
tw.Dispose();
}
}
}

private void button1_Click(object sender, EventArgs e)
{

throw new Exception(“Test Exception”);
}

private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
System.Diagnostics.
Process.Start(https://simonsteed.wordpress.com”);

}
}
}

Advertisements