using System;
using System.Collections;
using System.Diagnostics;
using System.Windows.Forms;
namespace KA
{
/// <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;}
}
}
}