Click here to Skip to main content
15,894,343 members
Articles / Programming Languages / C#

The Application Automation Layer - Design And Implementation of The Bootstrap Loader And The Component Manager

Rate me:
Please Sign up or sign in to vote.
4.39/5 (21 votes)
1 Jan 200314 min read 172.4K   494   128  
This article describes in detail the design considerations for the Bootstrap Loader and the Component Manager.
using System;
using System.Collections;
using System.Diagnostics;
using System.Windows.Forms;

namespace AAL
{
	/// <summary>
	/// Defines a custom type that represents a shorthand key to look up the
	/// problem/reasons dictionary element associated with this key.
	/// </summary>
	public class DbgKey
	{
		private string key;
		
		/// <summary>
		/// Gets or sets the name of the key.
		/// </summary>
		/// <value>
		/// The name of the key.
		/// </value>
		public string Name
		{
			get {return key;}
			set {key=value;}
		}
		
		/// <summary>
		/// Constructor.
		/// </summary>
		/// <param name="key"></param>
		public DbgKey(string key)
		{
			this.key=key;
		}
	}
	
	/// <summary>
	/// Container for a problem and an array describing possible reasons
	/// associated with the problem.
	/// </summary>
	public class ProblemReason
	{
		string problem;
		string[] reasons;

		/// <summary>
		/// Constructor.
		/// </summary>
		/// <param name="problem">A description of the problem.</param>
		/// <param name="reasonArray">An array of possible reasons for the problem.</param>
		public ProblemReason(string problem, string[] reasonArray)
		{
			this.problem=problem;
			reasons=reasonArray;
		}

		/// <summary>
		/// Get the description of the problem.
		/// </summary>
		/// <returns>Description of the problem.</returns>
		public string GetProblem()
		{
			return problem;
		}

		/// <summary>
		/// Get the array of reasons associated with the problem.
		/// </summary>
		/// <returns>Gets the reasons.</returns>
		public string[] GetReasons()
		{
			return reasons;
		}
	}

	/// <summary>
	/// A class representing the collection of problem/reason descriptions.
	/// </summary>
	public class DbgProblemCollection
	{
		private SortedList probs;
		
		/// <summary>
		/// Constructor.
		/// </summary>
		public DbgProblemCollection()
		{
			probs=new SortedList();
		}
		
		/// <summary>
		/// Associates a problem description with a shorthand "key".
		/// </summary>
		/// <param name="key">The shorthand key.</param>
		/// <param name="problem">The full description of the problem.</param>
		/// <param name="reasonList">A string array representing possible reasons for the problem.</param>
		public void Add(DbgKey key, string problem, string[] reasonList)
		{
//			Dbg.Assert(!probs.Contains(key), new DbgKey("DbgMgrMultipleKeys"));
			probs.Add(key.Name, new ProblemReason(problem, reasonList));
		}
		
		/// <summary>
		/// Test if the problem collection contains the specified key.
		/// </summary>
		/// <param name="key"></param>
		/// <returns>True if the key exists in the problem collection, false otherwise.</returns>
		public bool Contains(DbgKey key)
		{
			return probs.Contains(key.Name);
		}
		
		/// <summary>
		/// Gets the number of problems in the collection.
		/// </summary>
		public int Count
		{
			get
			{
				return probs.Count;
			}
		}
		
		/// <summary>
		/// Given the index (the key), returns a ProblemReason object.  The key is the indexer to the problem DbgProblemCollection.
		/// </summary>
		public ProblemReason this[DbgKey key]
		{
			get
			{
				return (ProblemReason)probs[key.Name];
			}
		}
	}
	
	/// <summary>
	/// Combines the functionality of the Trace and Debug classes into a single class coordinated by the TRACE and DEBUG attributes.
	/// This class extends the built in .NET functionality by adding an unhandled exception handler and a dictionary of problems
	/// and reasons, which provides useful information to the end user when an exception is thrown or an assertion occurs.
	/// <br></br>
	/// <br></br>
	/// This class also provides Warn and Fail methods that can be used in conjunction with handled exceptions, either to warn
	/// the end-user of a system problem or to terminate the application with a meaningful message.  Both the Warn and Fail methods
	/// use the problem/reason dictionary to provide meaningful information to the user.
	/// <br></br>
	/// <br></br>
	/// Using this class, all Writexxx methods are available in debug mode.  In release mode, only Assert functions are enabled.
	/// This ensures that output traces are only available in debug mode and not in released projects, which can compromise
	/// your application or system security.
	/// </summary>
	public class Dbg
	{
		// The debug extensions:
		private static DbgProblemCollection problems=new DbgProblemCollection();
	
		/// <summary>
		/// Gets the problem collection.
		/// </summary>
		/// <value>
		/// The problem collection.
		/// </value>
		public static DbgProblemCollection Problems
		{
			get {return problems;}
		}

		/// <summary>
		/// Initializes the Dbg object.  This consists of adding an unhandled exception handler and a internal key
		/// named "_NEVER_FAIL_" which is used by the Verify method.
		/// </summary>
		public static void Initialize()
		{
			AppDomain.CurrentDomain.UnhandledException+=new UnhandledExceptionEventHandler(DbgExceptionHandler);

			problems.Add(
				new DbgKey("_NEVER_FAIL_"),
				"An unexpected failure has occurred where it should not have.",
				new string[] {
					"Disk space is getting low.",
					"System memory is getting low.",
					"Required resources are unavailable.",
					"A virus is affecting system resources."});

			problems.Add(
				new DbgKey("DbgMgrMultipleKeys"),
				"Duplicate problem/reason key.",
				new string[] {
					"The same key cannot be used for different entries in the problem/reason dictionary.",
					"An object is instantiated more than once that should only be instantiated once."});
					
		}

		// ************** ALWAYS AVAILABLE FUNCTIONS **********************
		
		/// <summary>
		/// The private handler for exceptions that are not handled in catch-try blocks.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="args"></param>
		private static void DbgExceptionHandler(object sender, UnhandledExceptionEventArgs args)
		{
			Exception e=(Exception) args.ExceptionObject;
			Trace.WriteLine("Exception: "+e.Message+"\n"+e.GetType()+"\nStack Trace:\n"+e.StackTrace);
			MessageBox.Show(
				"A fatal problem has occurred.\n"+e.Message+"\nin: "+e.GetType(),
				"Program Stopped",
				MessageBoxButtons.OK,
				MessageBoxIcon.Stop,
				MessageBoxDefaultButton.Button1);
			Trace.Close();
			Process.GetCurrentProcess().Kill();
		}

		/// <summary>
		/// Displays a message indicating a fatal problem has occurred, then terminates
		/// the program.  It is suitable for displaying informative messages resulting from trapped
		/// exceptions that cannot be recovered.
		/// </summary>
		/// <param name="b">The result of a boolean test.</param>
		/// <param name="key">The problem/reason key.</param>
		/// <param name="error">The error text, usually the error text associated with an exception.</param>
		public static void Fail(bool b, DbgKey key, string error)
		{
			if (!b)
			{
				Trace.WriteLine("Fail: "+key.Name+"\n"+"\tInfo: "+error);
				if (problems.Contains(key))
				{
					string explanation=GetExplanation(key);
					MessageBox.Show(
						error+"\n\n"+explanation,
						"Program Stopped",
						MessageBoxButtons.OK,
						MessageBoxIcon.Stop,
						MessageBoxDefaultButton.Button1);
				}
				else
				{
					MessageBox.Show(
						error+"\n\n"+"A fatal problem has occurred.\n\nReference: "+key.Name,
						"Program Stopped",
						MessageBoxButtons.OK,
						MessageBoxIcon.Stop,
						MessageBoxDefaultButton.Button1);
				}
				Trace.Close();
				Process.GetCurrentProcess().Kill();
			}
		}

		/// <summary>
		/// Displays a message indicating a recoverable problem has occurred.  This is suitable
		/// for displaying informative messages resulting from a recoverable exception.
		/// </summary>
		/// <param name="b">The result of a boolean test.</param>
		/// <param name="key">The problem/reason key.</param>
		/// <param name="error">The error text, usually the error text associated with an exception.</param>
		public static void Warn(bool b, DbgKey key, string error)
		{
			if (!b)
			{
				Trace.WriteLine("Warning: "+key.Name+"\n"+"\tInfo: "+error);
				if (problems.Contains(key))
				{
					string explanation=GetExplanation(key);
					MessageBox.Show(
						error+"\n\n"+explanation,
						"Warning",
						MessageBoxButtons.OK,
						MessageBoxIcon.Warning,
						MessageBoxDefaultButton.Button1);
				}
				else
				{
					MessageBox.Show(
						error+"\n\n"+"A problem has occurred that should be corrected.\n\nReference: "+key.Name,
						"Warning",
						MessageBoxButtons.OK,
						MessageBoxIcon.Warning,
						MessageBoxDefaultButton.Button1);
				}
			}
		}

		/// <summary>
		/// This method similar to the VERIFY macro in C++, which can be used
		/// for testing things that should never fail.
		/// <br></br>
		/// <br></br>
		/// This function asserts in DEBUG and TRACE modes and does nothing if 
		/// both DEBUG and TRACE flags are turned off.  This is the best we can
		/// achieve because the [Conditional] attribute doesn't let us use boolean
		/// logic to say DEBUG AND NOT TRACE, and I don't want to introduce a third
		/// attribute!
		/// <br></br><br></br>
		/// A good use of this function is when the programmer needs to verify
		/// his/her understanding of the return values of an API function.
		/// </summary>
		/// <param name="b">The result of a boolean test.</param>
		public static void Verify(bool b)
		{
			Assert(b, new DbgKey("_NEVER_FAIL_"));
		}

		/// <summary>
		/// Flag the debugger to break at the next instruction.
		/// </summary>
		public static void Break()
		{
			Debugger.Break();
		}

		/// <summary>
		/// Put together the list of possible reasons for the particular problem.
		/// </summary>
		/// <param name="key">The problem/reason key.</param>
		/// <returns>A string enumerating all the reasons associated with the problem.</returns>
		private static string GetExplanation(DbgKey key)
		{
			ProblemReason ps=problems[key];
			string explanation=ps.GetProblem()+"\n\nPossible reasons:\n\n";
			int n=1;
			foreach (string sol in ps.GetReasons())
			{
				explanation+="  "+n.ToString()+". "+sol+"\n";
				++n;
			}
			return explanation;
		}

		// ************** TRACE FLAG ON (DEFAULT RELEASE MODE) **********************

		/// <summary>
		/// If the test fails, this function provides a meaningful message and then terminates
		/// the application.
		/// </summary>
		/// <param name="b">The result of a boolean test.</param>
		/// <param name="key">The key that is associated with the problem/reason description.</param>
		[Conditional("TRACE")]
		public static void Assert(bool b, DbgKey key)
		{
			if (!b)
			{
				Trace.WriteLine("Assert: "+key.Name);
				if (problems.Contains(key))
				{
					string explanation=GetExplanation(key);
					MessageBox.Show(
						explanation,
						"Program Stopped",
						MessageBoxButtons.OK,
						MessageBoxIcon.Stop,
						MessageBoxDefaultButton.Button1);
				}
				else
				{
					MessageBox.Show(
						"A fatal problem has occurred.\n\nReference: "+key.Name,
						"Program Stopped",
						MessageBoxButtons.OK,
						MessageBoxIcon.Stop,
						MessageBoxDefaultButton.Button1);
				}
				Trace.Close();
				Process.GetCurrentProcess().Kill();
			}
		}

		/// <summary>
		/// Passes through to the System.Diagnostics.Trace.Assert method
		/// </summary>
		[Conditional("TRACE")]
		public static void Assert(bool b)
		{
			Trace.Assert(b);
		}
		
		/// <summary>
		/// Passes through to the System.Diagnostics.Trace.Assert method
		/// </summary>
		[Conditional("TRACE")]
		public static void Assert(bool b, string s)
		{
			Trace.Assert(b, s);
		}
		
		/// <summary>
		/// Passes through to the System.Diagnostics.Trace.Assert method
		/// </summary>
		[Conditional("TRACE")]
		public static void Assert(bool b, string s1, string s2)
		{
			Trace.Assert(b, s1, s2);
		}

		/// <summary>
		/// Passes through to the System.Diagnostics.Trace.Fail method
		/// </summary>
		[Conditional("TRACE")]
		public static void Fail(string s)
		{
			Trace.Fail(s);
		}
		
		/// <summary>
		/// Passes through to the System.Diagnostics.Trace.Fail method
		/// </summary>
		[Conditional("TRACE")]
		public static void Fail(string s1, string s2)
		{
			Trace.Fail(s1, s2);
		}
		
		// ************** DEBUG FLAG ON (DEFAULT DEBUG MODE) **********************

		/// <summary>
		/// Passes through to the System.Diagnostics.Debug.LogOutput method
		/// </summary>
		[Conditional("DEBUG")]
		public static void LogOutput(string fn)
		{
			Debug.Listeners.Add(new TextWriterTraceListener(fn));
			DateTime dt=DateTime.Now;
			Debug.WriteLine("\n\nDebug Logging Initialized: "+dt.ToString());
		}

		/// <summary>
		/// Passes through to the System.Diagnostics.Debug.Close method
		/// </summary>
		[Conditional("DEBUG")]		
		public static void Close()
		{
			Trace.Close();
		}
		
		/// <summary>
		/// Passes through to the System.Diagnostics.Debug.Flush method
		/// </summary>
		[Conditional("DEBUG")]		
		public static void Flush()
		{
			Debug.Flush();
		}
		
		/// <summary>
		/// Passes through to the System.Diagnostics.Debug.Indent method
		/// </summary>
		[Conditional("DEBUG")]		
		public static void Indent()
		{
			Debug.Indent();
		}
		
		/// <summary>
		/// Passes through to the System.Diagnostics.Debug.Unindent method
		/// </summary>
		[Conditional("DEBUG")]		
		public static void Unindent()
		{
			Debug.Unindent();
		}
		
		// Write
		
		/// <summary>
		/// Passes through to the System.Diagnostics.Debug.Write method
		/// </summary>
		[Conditional("DEBUG")]
		public static void Write(object obj)
		{
			Debug.Write(obj);
		}
		
		/// <summary>
		/// Passes through to the System.Diagnostics.Debug.Write method
		/// </summary>
		[Conditional("DEBUG")]		
		public static void Write(string s)
		{
			Debug.Write(s);
		}
		
		/// <summary>
		/// Passes through to the System.Diagnostics.Debug.Write method
		/// </summary>
		[Conditional("DEBUG")]		
		public static void Write(object obj, string s)
		{
			Debug.Write(obj, s);
		}
		
		/// <summary>
		/// Passes through to the System.Diagnostics.Debug.Write method
		/// </summary>
		[Conditional("DEBUG")]		
		public static void Write(string s1, string s2)
		{
			Debug.Write(s1, s2);
		}
		
		// WriteIf
		
		/// <summary>
		/// Passes through to the System.Diagnostics.Debug.WriteIf method
		/// </summary>
		[Conditional("DEBUG")]		
		public static void WriteIf(bool b, object obj)
		{
			Debug.WriteIf(b, obj);
		}
		
		/// <summary>
		/// Passes through to the System.Diagnostics.Debug.WriteIf method
		/// </summary>
		[Conditional("DEBUG")]		
		public static void WriteIf(bool b, string s)
		{
			Debug.WriteIf(b, s);
		}
		
		/// <summary>
		/// Passes through to the System.Diagnostics.Debug.WriteIf method
		/// </summary>
		[Conditional("DEBUG")]		
		public static void WriteIf(bool b, object obj, string s)
		{
			Debug.WriteIf(b, obj, s);
		}
		
		/// <summary>
		/// Passes through to the System.Diagnostics.Debug.WriteIf method
		/// </summary>
		[Conditional("DEBUG")]		
		public static void WriteIf(bool b, string s1, string s2)
		{
			Debug.WriteIf(b, s1, s2);
		}
		
		// WriteLine
		
		/// <summary>
		/// Passes through to the System.Diagnostics.Debug.WriteLine method
		/// </summary>
		[Conditional("DEBUG")]		
		public static void WriteLine(object obj)
		{
			Debug.WriteLine(obj);
		}
		
		/// <summary>
		/// Passes through to the System.Diagnostics.Debug.WriteLine method
		/// </summary>
		[Conditional("DEBUG")]		
		public static void WriteLine(string s)
		{
			Debug.WriteLine(s);
		}
		
		/// <summary>
		/// Passes through to the System.Diagnostics.Debug.WriteLine method
		/// </summary>
		[Conditional("DEBUG")]		
		public static void WriteLine(object obj, string s)
		{
			Debug.WriteLine(obj, s);
		}
		
		/// <summary>
		/// Passes through to the System.Diagnostics.Debug.WriteLine method
		/// </summary>
		[Conditional("DEBUG")]		
		public static void WriteLine(string s1, string s2)
		{
			Debug.WriteLine(s1, s2);
		}
		
		// WriteLineIf
		
		/// <summary>
		/// Passes through to the System.Diagnostics.Debug.WriteLineIf method
		/// </summary>
		[Conditional("DEBUG")]		
		public static void WriteLineIf(bool b, object obj)
		{
			Debug.WriteLineIf(b, obj);
		}
		
		/// <summary>
		/// Passes through to the System.Diagnostics.Debug.WriteLineIf method
		/// </summary>
		[Conditional("DEBUG")]		
		public static void WriteLineIf(bool b, string s)
		{
			Debug.WriteLineIf(b, s);
		}
		
		/// <summary>
		/// Passes through to the System.Diagnostics.Debug.WriteLineIf method
		/// </summary>
		[Conditional("DEBUG")]		
		public static void WriteLineIf(bool b, object obj, string s)
		{
			Debug.WriteLineIf(b, obj, s);
		}
		
		/// <summary>
		/// Passes through to the System.Diagnostics.Debug.WriteLineIf method
		/// </summary>
		[Conditional("DEBUG")]		
		public static void WriteLineIf(bool b, string s1, string s2)
		{
			Debug.WriteLineIf(b, s1, s2);
		}

		// getters and setters
		
		/// <summary>
		/// Gets and sets the current value in the System.Diagnostics.Debug.AutoFlush parameter.
		/// </summary>
		public static bool AutoFlush
		{
			get {return Debug.AutoFlush;}
			set {Debug.AutoFlush=value;}
		}
		
		/// <summary>
		/// Gets and sets the current value in the System.Diagnostics.Debug.IndentLevel parameter.
		/// </summary>
		public static int IndentLevel
		{
			get {return Debug.IndentLevel;}
			set {Debug.IndentLevel=value;}
		}
		
		/// <summary>
		/// Gets and sets the current value in the System.Diagnostics.Debug.IndentSize parameter.
		/// </summary>
		public static int IndentSize
		{
			get {return Debug.IndentSize;}
			set {Debug.IndentSize=value;}
		}
		
		/// <summary>
		/// Gets current collection of trace listeners in the System.Diagnostics.Trace object.
		/// </summary>
		public static TraceListenerCollection Listeners
		{
			get {return Trace.Listeners;}
		}
		
	}
}

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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Architect Interacx
United States United States
Blog: https://marcclifton.wordpress.com/
Home Page: http://www.marcclifton.com
Research: http://www.higherorderprogramming.com/
GitHub: https://github.com/cliftonm

All my life I have been passionate about architecture / software design, as this is the cornerstone to a maintainable and extensible application. As such, I have enjoyed exploring some crazy ideas and discovering that they are not so crazy after all. I also love writing about my ideas and seeing the community response. As a consultant, I've enjoyed working in a wide range of industries such as aerospace, boatyard management, remote sensing, emergency services / data management, and casino operations. I've done a variety of pro-bono work non-profit organizations related to nature conservancy, drug recovery and women's health.

Comments and Discussions