Geeks With Blogs

News
Neat Stuff Read all my hurricane entries While you are here, visit the Geeks With Blogs main feed
Advertising
Links Status of the Navy
Channel 9

.NET Hobbyist Programmer Staying Confused in a Busy World

Update: A C# version of this base exception class is posted here.

This is an update to my previous post about a custom base exception class.  I spent some time in the interim reviewing the Microsoft Exception Management Application Block code and added some of the features that I found there.  Reassuringly, most of my design features matched and in some cases exceeded theirs.  Here is a listing of changes since that earlier version:

  • Added custom properties for the Windows Identity and an Additional Information collection.  This included the serialization, deserialization and ToString code.
  • Added error handling to the code which obtained the custom variable information.  This was particularly for security exceptions.
  • Added a security attribute to the GetObjectData routine.
  • Changed most properties from read-write to read only since they should never be changed.
  • Changed the Integer properties to String.

Hopefully, I am finished this superset of base exception class features.  If I think of something more I need I will add it, but I believe that this will be sufficient for my base exception class requirements.

It was evident that Google crawled GeeksWithBlogs a couple of days ago since there was a spike in hits on the previous version of this class.  I guess the soft and chewy center fulfilled someone's need.

===== Start File BaseException.vb =====

Option
 Explicit On
Option Strict On

Imports System.Runtime.Serialization

Namespace Exception

' Base Exception Class.  This is the base exception object from which to derive
' all application exceptions.  The hierarchy should be split below this class
' into technical and business related exceptions.
()> _
Public Class BaseException
Inherits System.Exception

#Region " Class Data "

Private m_AppDomainName As String
Private m_AssemblyName As String
Private m_CurrentProcessID As String
Private m_CurrentThreadID As String
Private m_CurrentThreadUser As String
Private m_MachineName As String
Private m_UTCDateTime As System.DateTime
Private m_WindowsIdentity As String
' Collection provided to store any extra information associated
' with the exception

Private m_AdditionalInformation As New _
 
System.Collections.Specialized.NameValueCollection
' These provide some code simplifications below
Private DateTimeType As System.Type = System.Type.GetType("DateTime")
Private StringType As System.Type = System.Type.GetType("String")

#End Region

#Region " Constructors "

' Default constructor
Public Sub New()
  MyBase.New()
  m_AssemblyName = System.Reflection.Assembly.GetCallingAssembly().FullName
  Me.Initialize()
  Me.DoLogging("")
End Sub

' Constructor accepting a single string error message
Public Sub New(ByVal message As String)
  MyBase.New(message)
  m_AssemblyName = System.Reflection.Assembly.GetCallingAssembly().FullName
  Me.Initialize()
  Me.DoLogging("")
End Sub

' Deserialization constructor for serialized data
Public Sub New(ByVal info As SerializationInfoByVal context As StreamingContext)
  MyBase.New(infocontext)
  Me.Initialize()
  ' Deserialize our custom properties after Initialize and before DoLogging
  With info
    m_UTCDateTime = .GetDateTime("seDateTime").ToUniversalTime
    m_AppDomainName = .GetString("seAppDomainName")
    m_AssemblyName = .GetString("seAssemblyName")
    m_CurrentProcessID = .GetString("seCurrentProcessID")
    m_CurrentThreadID = .GetString("seCurrentThreadID")
    m_CurrentThreadUser = .GetString("seCurrentThreadUser")
    m_MachineName = .GetString("seMachineName")
    m_WindowsIdentity = .GetString("seWindowsIdentity")
    m_AdditionalInformation = CType(.GetValue("additionalInformation"_
      GetType(System.Collections.Specialized.NameValueCollection)), _
      System.Collections.Specialized.NameValueCollection)
  End With
  Me.DoLogging("")
End Sub

' Constructor with a specified error message and a reference to the
' inner exception that is the cause of this exception
Public Sub New(ByVal message As StringByVal inner As System.Exception)
  MyBase.New(messageinner)
  m_AssemblyName = System.Reflection.Assembly.GetCallingAssembly().FullName
  Me.Initialize()
  Me.DoLogging("")
End Sub

#End Region

#Region " Overrides "

' This override is required to add our custom properties to the serialization stream
.Security.Permissions.SecurityPermission_
  System.Security.Permissions.SecurityAction.Demand_
  SerializationFormatter:=True)> _
Public Overrides Sub GetObjectData(ByVal info As SerializationInfoByVal context As StreamingContext)
' Serialize our custom properties
With info
  .AddValue("seDateTime"m_UTCDateTime.ToLocalTimeDateTimeType)
  .AddValue("seAppDomainName"m_AppDomainNameStringType)
  .AddValue("seAssemblyName"m_AssemblyNameStringType)
  .AddValue("seCurrentProcessID"m_CurrentProcessIDStringType)
  .AddValue("seCurrentThreadID"m_CurrentThreadIDStringType)
  .AddValue("seCurrentThreadUser"m_CurrentThreadUserStringType)
  .AddValue("seMachineName"m_MachineNameStringType)
  .AddValue("seWindowsIdentity"m_WindowsIdentityStringType)
  .AddValue("seAdditionalInformation"_
    m_AdditionalInformation_
    GetType(System.Collections.Specialized.NameValueCollection))
End With
' Call the base routine
MyBase.GetObjectData(infocontext)
End Sub

' This override is required to be able to display our custom properties
Public Overrides Function ToString() As String
Dim sb As System.Text.StringBuilder = New System.Text.StringBuilder
Dim nl As String = Environment.NewLine
' Make sure we get the standard output
sb.Append(MyBase.ToString)
' Since the standard output can be nothing, we only add our variables when necessary
If (sb.Length > 0Then
  With sb
    ' Change this format as required
    .Append(nl + nl)
    .Append("Local Date Time: " + m_UTCDateTime.ToLocalTime.ToString + nl)
    .Append("Windows Identity: " + m_WindowsIdentity + nl)
    .Append("Machine Name: " + m_MachineName + nl)
    .Append("App Domain Name: " + m_AppDomainName + nl)
    .Append("Assembly Name: " + m_AssemblyName + nl)
    .Append("Current Process ID: " + m_CurrentProcessID + nl)
    .Append("Current Thread ID: " + m_CurrentThreadID + nl)
    .Append("Current Thread User: " + m_CurrentThreadUser + nl)
    ' Additional info collection special handling
    If (m_AdditionalInformation.Count > 0Then
      .Append("Additional Information: " + m_CurrentThreadUser + nl)
      Dim s As String
      For Each s In m_AdditionalInformation.AllKeys
        If (m_AdditionalInformation(s).Length > 0Then
          .Append("   " + s + " = " + m_AdditionalInformation(s+ nl)
        Else
          .Append("   " + s + nl)
        End If
      Next
    End If
  End With
End If
Return sb.ToString
End Function

#End Region

#Region " Private Routines "

' Routine called from all Constructors to initialize our custom variables
Private Sub Initialize()
  Dim errorPermissionDenied As String = "Permission Denied"
  Dim errorInfoAccessException As String = "Information could not be accessed."
  m_UTCDateTime = System.DateTime.UtcNow
  ' -----
  Try
    m_AppDomainName = System.AppDomain.CurrentDomain.FriendlyName
  Catch e As System.Security.SecurityException
    m_AppDomainName = errorPermissionDenied
  Catch
    m_AppDomainName = errorInfoAccessException
  End Try
  ' -----
  Try
    m_CurrentProcessID = Process.GetCurrentProcess().Id.ToString
  Catch e As System.Security.SecurityException
    m_CurrentProcessID = errorPermissionDenied
  Catch
    m_CurrentProcessID = errorInfoAccessException
  End Try
  ' -----
  Try
    m_AssemblyName = System.Reflection.Assembly.GetExecutingAssembly().FullName
  Catch e As System.Security.SecurityException
    m_AssemblyName = errorPermissionDenied
  Catch
    m_AssemblyName = errorInfoAccessException
  End Try
  ' -----
  Try
    m_CurrentThreadID = AppDomain.GetCurrentThreadId().ToString
  Catch e As System.Security.SecurityException
    m_CurrentThreadID = errorPermissionDenied
  Catch
    m_CurrentThreadID = errorInfoAccessException
  End Try
  ' -----
  Try
    m_CurrentThreadUser = System.Threading.Thread.CurrentPrincipal.Identity.Name
  Catch e As System.Security.SecurityException
    m_CurrentThreadUser = errorPermissionDenied
  Catch
    m_CurrentThreadUser = errorInfoAccessException
  End Try
  ' -----
  Try
    m_MachineName = System.Environment.MachineName
  Catch e As System.Security.SecurityException
    m_MachineName = errorPermissionDenied
  Catch
    m_MachineName = errorInfoAccessException
  End Try
  ' -----
  Try
    m_WindowsIdentity = System.Security.Principal.WindowsIdentity.GetCurrent().Name
  Catch e As System.Security.SecurityException
    m_WindowsIdentity = errorPermissionDenied
  Catch
    m_WindowsIdentity = errorInfoAccessException
  End Try
End Sub

Private Sub DoLogging(ByVal message As String)
  ' Placeholder for future logging
End Sub

#End Region

#Region " Properties "

' Collection allowing unlimited additional information to be added to the exception
Public Property AdditionalInformation() As System.Collections.Specialized.NameValueCollection
  Get
    Return m_AdditionalInformation
  End Get
  Set(ByVal Value As System.Collections.Specialized.NameValueCollection)
    m_AdditionalInformation = Value
  End Set
End Property

' The application domain name where the exception occurred
Public ReadOnly Property AppDomainName() As String
  Get
    Return m_AppDomainName
  End Get
End Property

' Assembly name in which the exception occurred
Public ReadOnly Property AssemblyName() As String
  Get
    Return m_AssemblyName
  End Get
End Property

Public ReadOnly Property CurrentProcessID() As String
  Get
    Return m_CurrentProcessID
  End Get
End Property

Public ReadOnly Property CurrentThreadID() As String
  Get
    Return m_CurrentThreadID
  End Get
End Property

Public ReadOnly Property CurrentThreadUser() As String
  Get
    Return m_CurrentThreadUser
  End Get
End Property

' Date and time of the exception
Public ReadOnly Property DateTime() As DateTime
  Get
    Return m_UTCDateTime.ToLocalTime
  End Get
End Property

' Machine name where the exception occurred
Public ReadOnly Property MachineName() As String
  Get
    Return m_MachineName
  End Get
End Property

' Windows identity under which the code was running
Public ReadOnly Property WindowsIdentityName() As String
  Get
    Return m_WindowsIdentity
  End Get
End Property

#End Region

End Class

End Namespace


===== End File BaseException.vb =====

Posted on Thursday, April 1, 2004 6:26 PM Programming | Back to top


Comments on this post: Custom Base Exception Class - V2 (VB)

# re: Custom Base Exception Class - V2
Requesting Gravatar...
Can you post a C# version of this code? I tried using an online VB-to-C# converter (http://www.developerfusion.com/utilities/convertvbtocsharp.aspx) but it butchered your code.

Thanks, Simon.
Left by Simon Wallis on Aug 10, 2004 11:27 AM

# re: Custom Base Exception Class - V2
Requesting Gravatar...
Try this one (Instant C#).
Left by Dave on Sep 16, 2004 11:35 AM

# re: Custom Base Exception Class - V2
Requesting Gravatar...
Why do you set the value of m_AssemblyName = GetCallingAssembly().FullName in all of your constructors and then turn right around and set the value of m_AssemblyName = GetExecutingAssembly().FullName?

Just curious what your point is with this particular code.
Left by Cedric on Dec 29, 2004 3:09 AM

# re: Custom Base Exception Class - V2
Requesting Gravatar...
I would need to go back and look at my test app again to step through and see why I did both. Two things I do remember are that GetCallingAssembly().FullName needed to be in the constructors in order to return a useful value, and that the internal exception handling was added later. The dual assignment is likely an error.
Left by Mark on Dec 29, 2004 6:47 AM

Your comment:
 (will show your gravatar)


Copyright © Mark Treadwell | Powered by: GeeksWithBlogs.net