C# .NET Exception : AppDomain.UnhandledException
When an unhandled exception is manifested in a Console application, the AppDomain .UnhandledException is raised. Subscribe to the event to clean up the resources of the application, such as closing files and relinquishing data connections. You might also record the unhandled exception in the event log or another location. It is important to note that the exception is not caught in the AppDomain.UnhandledException handler. After the handler finishes, the unhandled exception causes the application to be terminated. The AppDomain.UnhandledException event is triggered only in the initial application domain; it is ignored in other application domains.
Subscribe to the AppDomain.UnhandledException event with an UnhandledExceptionEventHandler delegate. The delegate has two parameters. The object parameter is the AppDomain object of the initial application domain. The UnhandledExceptionEventArgs parameter contains the specifics of the unhandled exception. This is the syntax of the UnhandledExceptionEventHandler:
UnhandledExceptionEventHandler syntax:
-
void UnhandledExceptionEventHandler(object sender, UnhandledExceptionEventArgs e)
UnhandledExceptionEventArgs offers the IsTerminating and ExceptionObject properties. IsTerminating is a Boolean property indicating the status of the application. If true, the application is terminating because of the exception. If false, the application survives the exception. In .NET 2.0, this property is always true. Unhandled exceptions on both managed and unmanaged threads terminate an application. This is cleaner than the .NET 1.1 unhandled exception model, where exceptions raised on managed threads were nonfatal. The Exception property is an exception object for the unhandled exception. Inexplicably, this property is an object type, not an Exception type. Cast the property to the Exception type to access the details of the exception.
In the following Console application, the OnUnhandledException method is added to the AppDomain.UnhandledException event. When the subsequent divide by zero exception occurs, the OnUnhandledException method is called.
using System;
namespace Examples.Exceptions
{
public class Program
{
public static void Main()
{
AppDomain.CurrentDomain.UnhandledException +=
new UnhandledExceptionEventHandler(
OnUnhandledException);
int vara = 5, varb = 0;
vara /= varb;
Console.ReadKey();
}
public static void OnUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
string application_name = sender.ToString();
Exception except = (Exception)e.ExceptionObject;
string errormessage = "Application " + application_name + " [ Exception " + except.Message + " ]";
Console.WriteLine(errormessage);
}
}
}
Log4Net Cofiguration
I had to post this one long days ago as I promised in one of my earlier post about Log4Net. Sorry for this late. I am not a man of words. Thanks to Chris, for reminding me about this post. There are lots of things in the configuration of Log4Net. I will only about the commonly used sections.
<log4net> <appender name="LogFileAppender" type="log4net.Appender.FileAppender"> <param name="File" value="D:\Temp\ApplicationLog.log" /> <param name="AppendToFile" value="true" /> <layout type="log4net.Layout.PatternLayout"> <param name="Header" value="" /> <param name="Footer" value="" /> <param name="ConversionPattern" value="%d [%t] %-5p %m%n" /> </layout> </appender> <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender" > <layout type="log4net.Layout.PatternLayout"> <param name="Header" value="[Header]\r\n" /> <param name="Footer" value="[Footer]\r\n" /> <param name="ConversionPattern" value="%d [%t] %-5p %m%n" /> </layout> </appender> <root> <level value="DEBUG" /> <appender-ref ref="LogFileAppender" /> <appender-ref ref="ConsoleAppender" /> </root> </log4net>
Log4Net is configured in the configuration section “log4net”, specified with the tag <log4net>. Under this tag, you can find two tags, <appender> and <root>. (There could be more actually. but I did not need them yet.)
<appender> section says where and how the log should be written. It could be in the console, in a file, or database. Logs can even be sent to an email address. So, depending on you needs, you have to configure the appender. Under <log4net> element one or more appenders can be defined. I am interested to describe some of the appenders. Please remind me if I forget to do so.
<root> section is to say, which appender will be used to log, and what types of log will be written.Only one root logger element may only be defined and it must be a child of <log4net> element. To define te log-level, you have to specify your desired log-level in the <level> element. You can set the ‘value’ attribute to any of the five log-levels. The levels are as follows hierarchically:
- FATAL
- ERROR
- WARN
- INFO
- DEBUG
Once you specify the level of the log in the ‘value’ attribute, logs for that level and the levels above will be written.
C# .NET Exceptions : Application.ThreadException
In a Windows Forms application, the windows procedure raises the Application.ThreadException event upon an unhandled exception. Subscribe to the ThreadException event to handle the unhandled exception. The subscriber is an exception handler, which prevents the application from being terminated. Do not propagate an exception caught in this manner in the unhandled exception handler. The new exception is unprotected and will likely terminate the application. After the unhandled exception handler completes, execution continues at the next message in the message pump.
Subscribe to the ThreadException event with a ThreadExceptionEventHandler delegate, which has two parameters. The object parameter is the thread object of the thread that raised the exception. The ThreadExceptionEventArg parameter of the System.Threading namespace contains the exception that was unhandled. This is the signature of the ThreadExceptionEventHandler:
ThreadExceptionEventHandler syntax:
void ThreadExceptionEventHandler(object sender, ThreadExceptionEventArgs e)
In the following code, the OnThreadException handler is added to the ThreadException event. The bthException_Click method raises an unhandled divide by zero exception. The unhandled exception is then handled in the OnThreadException method, which displays an informative message. Run the application in release mode for the expected results. Otherwise, the Visual Studio debugger intercedes the exception.
private void btnException_Click(object sender, EventArgs e)
{
int vara = 5, varb = 0;
vara /= varb;
}
private void Form1_Load(object sender, EventArgs e)
{
Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(OnThreadException);
}
void OnThreadException(object sender, ThreadExceptionEventArgs e)
{
Thread t = (Thread)sender;
Exception threadexception = e.Exception;
string errormessage = "Thread ID: " + t.ManagedThreadId.ToString() + " [ " + threadexception.Message + " ]";
MessageBox.Show(errormessage);
}
Why Log4Net?
Microsoft has its own application block for logging. So, why should we use Log4Net instead of that?
Log4Net has some advantages over Enterprise Library Logging Application Block. These made Log4Net more preferable.
Works with .NET 1.0 & 1.1 - The much improved logging of EntLib 2.0 and above is only available if your application is running on .NET 2.0 or greater. log4net however works on all versions of .NET.
Simpler install - When using the Enterprise Library there are some services you really should install. This is as simple as running a bat file included with EntLib but it does complicate your deployment process.
Slightly faster - log4net was significantly quicker than EntLib 1.1 logging. EntLib 2.0 onwards has improved its performance but log4net remains slightly faster. A benchmark I found while researching my presentation had EntLib taking approximately 5-6 seconds to log 100,000 entries while log4net took about 3 seconds. Does the speed difference matter? Probably not. However log4net net does support…
Appender Buffering - Buffering support with some appenders lets log4net queue up log entries and write them in a single go. If you are writing entries to the database then buffering is a good way to improve performance.
More platforms - Enterprise Library does not support the .NET Compact Framework while log4net does.
Remote Exceptions in C# .NET
Exceptions sometime occur in remote code. An exception that is raised in a different application domain is a remote exception. Remote exceptions include exceptions thrown in a .NET Remoting application or a Web service application. Exceptions that cross application domains must be serialized to maintain the state. System exceptions are serializable. However, you need to make application exceptions serializable.
Follow these steps to serialize an application exception:
-
Adorn the application exception with the serializable attribute.
-
Implement a two-argument constructor with a SerializationInfo and StreamingContext parameter. Deserialize the exception with the SerializationInfo parameter, which is a state bag. Retrieve state information of the exception using the Get methods of the SerializationInfo object. The StreamingContext parameter provides additional data about the source or target of the serialization process. In addition, call the same constructor in the base class to allow the base class to deserialize its state.
-
Implement the GetObjectData method to serialize the exception. The method also has two parameters: Serialization and StreamingContext. Use the Serialization.AddValue to serialize the state of the exception. Invoke GetObjectData on the base class to allow it to serialize itself.
-
For the exception to be available in the client assembly, share the assembly through a global assembly cache or an application configuration file. If the assembly is not shared, the assembly must be copied into the private directory of the client application.
CustomException is an application exception that supports remoting. There is one property, prop_Time, which is serialized in GetObjectData and deserialized in the two-argument constructor:
using System;
using System.Reflection;
using System.Runtime.Serialization;
[assembly: AssemblyVersion("1.1.0.0")]
[assembly: AssemblyCultureAttribute("")]
namespace Examples.Exceptions
{
[Serializable]
public class CustomException : Exception
{
public CustomException() : base("custom exception", null)
{
prop_Time = DateTime.Now.ToLongDateString() + " " + DateTime.Now.ToShortTimeString();
}
protected CustomException(SerializationInfo info, StreamingContext context) : base(info, context)
{
prop_Time = info.GetString("Time");
}
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("Time", prop_Time, typeof(string));
base.GetObjectData(info, context);
}
protected string prop_Time = null;
public string Time
{
get
{
return prop_Time;
}
}
}
}
In Microsoft Visual Studio, the assembly attributes, such as AssemblyVersion, are found in the AssemblyInfo.cs file.
Application Exceptions in C# .NET
Application exceptions are custom exceptions and are thrown by the application, not by the CLR. Application exceptions are derived from System.ApplicationException or System.Exception. System.ApplicationException adds nothing to System.Exception. While System.SystemException is a marker for system exceptions, System.ApplicationException brands application exceptions. A custom exception derived from System.Exception accomplishes the same feat. When several custom exceptions are planned, create a custom base exception class to categorize the exceptions. For convenience and maintainability, deploy application exceptions together in a separate assembly.
Do not create an application exception for an existing exception. Research the available system exceptions to avoid replicating an existing exception.
These are the steps for creating an application exception:
-
Name the application exception. As a best practice, the class name should have the Exception suffix, as in ApplicationException.
-
Derive the application exception from System.Exception.
-
Define constructors that initialize the state of the application exception. This includes initializing members inherited from the base class.
-
Within the application exception, refine System.Exception as desired, such as by adding attributes that further delineate this specific exception.
To raise an application exception, use the throw statement. You can also throw system exceptions. Thrown exceptions are considered software exceptions. The CLR treats software exceptions as standard exceptions.
throw syntax:
throw exceptioninstance1;
throw2;
The second syntax is specialized: It is available in the catch block, but nowhere else. This version of the throw statement rethrows as an exception caught in the catch block. However, the best policy is to add additional context to an exception before propagating the exception object. Propagating exceptions is reviewed later in this chapter.
Application exceptions are typically prompted by an exceptional event. What is an exceptional event? A strict definition does not exist. You define the basis of the event using whatever criteria are appropriate. Remember, raising an exception simply for transfer of control or a nonexceptional event is bad policy. In an application, the following could be considered exceptional events where throwing an application exception is warranted:
-
Constructor fails to initialize the state of an object.
-
A property does not pass validation.
-
Null parameters.
-
An exceptional value is returned from a function.
ConstructorException is an application exception. Throw this exception when a constructor fails. It refines the System.Exception base class with name of the type and time of exception. In addition, the Message property is assigned a congruous message. This is the code for the ConstructorException class:
using System;
namespace Examples.Exceptions
{
public class ConstructorException : Exception
{
public ConstructorException(object origin) : this(origin, null)
{
}
public ConstructorException(object origin, Exception innerException) : base("Exception in constructor", innerException)
{
prop_Typename = origin.GetType().Name;
prop_Time = DateTime.Now.ToLongDateString() + " " + DateTime.Now.ToShortTimeString();
}
protected string prop_Typename = null;
public string Typename
{
get
{
return prop_Typename;
}
}
protected string prop_Time = null;
public string Time
{
get
{
return prop_Time;
}
}
}
}
This code uses the ConstructorException class:
using System;
namespace Examples.Exceptions
{
public class Program
{
public static void Main()
{
try
{
ZClass obj = new ZClass();
}
catch (ConstructorException except)
{
Console.WriteLine(except.Message);
Console.WriteLine("Typename: " + except.Typename);
Console.WriteLine("Occured: " + except.Time);
}
Console.ReadKey();
}
}
class ZClass
{
public ZClass()
{
// initialization fails
throw new ConstructorException(this);
}
}
}
C# .NET System.Exception Properties
System.Exception has a full complement of attributes providing information on the exception. The following table describes the properties of the Exception class.
|
Property |
Description |
Type |
Read/Write |
|
Data |
Returns a dictionary collection that provides optional user-defined details of exception. |
IDictionary |
R |
|
HelpLink |
Link to a help file for the exception. |
string |
R/W |
|
HResult |
The HRESULT, which is a 32-bit error code common to COM, assigned to the exception. This is a protected property. |
int |
R/W |
|
InnerException |
When exceptions are propagated, the inner exception represents the previous exception. |
Exception |
R |
|
Message |
User-friendly message describing the exception. |
string |
R |
|
Source |
Name of application or object where exception occurred. |
string |
R/W |
|
StackTrace |
String representation of the call stack when the exception occurred. |
string |
R |
|
TargetSite |
Reference to the method where exception is raised. |
MethodBase |
R |
The Message and InnerException properties are settable in constructors of the Exception class.
The following code uses some of the properties of the Exception class. In Main, MethodA is called, and an exception is raised. The exception is caught and handled in Main. In the catch block, the exception flag is set to false. Leveraging the TargetSite property, MethodA is then called again successfully. The TargetSite property returns a MethodBase type, which can be used to late bind and invoke a method.
using System;
using System.Reflection;
namespace Example.Exceptions
{
public class Starter
{
public static bool bException = true;
public static void Main()
{
try
{
MethodA();
}
catch (Exception except)
{
Console.WriteLine(except.Message);
bException = false;
except.TargetSite.Invoke(null, null);
Console.ReadKey();
}
}
public static void MethodA()
{
if (bException)
{
throw new ApplicationException("exception message");
}
}
}
}
C# .NET System.Exception Functions
System.Exception has four constructors:
- public Exception1()
- public Exception2(string message)
- public Exception3(string message, Exception innerException)
- protected Exception4(Serialization info, StreamingContext context)
Exception1 is the default constructor. Exception2 constructor sets the user-friendly message of the exception. Exception3 constructor also sets the inner exception, which is the originating exception. Exception4 deserializes an exception raised remotely.
The Exception class has several other helpful functions. The table lists the important methods of the class.
|
Table: Exception Methods |
|
|
Method Name |
Result |
|
GetBaseException |
Returns the root exception in a chain of exception objects |
|
GetObjectData |
Serializes data of the Exception class |
|
GetType |
Returns the type of the exception |
|
ToString |
Concatenates the name of the exception object with the user-friendly message |
The following code calls GetBaseException and outputs the error message of the initial exception. If the current exception is the first exception in a chain of exceptions, GetBaseException returns null. Alternatively, you can walk InnerException properties back to the first exception.
using System;
namespace Example.Exceptions
{
public class Program
{
public static void Main()
{
try
{
MethodA();
}
catch (Exception except)
{
Exception original = except.GetBaseException();
Console.WriteLine(original.Message);
}
Console.ReadKey();
}
public static void MethodA()
{
try
{
MethodB();
}
catch (Exception except)
{
throw new ApplicationException("Inner Exception", except);
}
}
public static void MethodB()
{
throw new ApplicationException("Innermost Exception");
}
}
}
C# .NET Exceptions : Propagating Exceptions
Exceptions are not always handled locally where the exception is caught. It is sometimes beneficial to catch an exception and then propagate the exception. Propagating an exception is catching and then rethrowing the exception. Rethrowing an exception continues the search along the call stack to find an appropriate handler. Here are some reasons to propagate an exception:
-
There is a centralized handler for the exception. There are several reasons for implementing centralized handlers, including code reuse. Instead of handling an exception in various locations in an application, concentrate code for certain exceptions in a centralized handler. Wherever the exception is raised, the proper response is to record the exception and then delegate to the centralized handler. A central handler can be used to handle all exceptions in a single place.
-
Resources required to handle the exception are not available locally. For example, an exception is raised because of an invalid database connection. However, the correct connection string is read from a file not available where the exception occurs. The solution is to propagate the exception to a handler that has access to the file resource.
-
Propagate unwanted exceptions caught in the umbrella of the exception filter. This would be useful for catching all DataException types with the exception of the DuplicateNameException. One solution would be to write 12 individual catches—one for each of the data exceptions except for the DuplicateNameException exception. A better solution is to catch the DataException type and propagate the DuplicateNameException when necessary. This is one catch statement versus 12 catch statements and eliminates redundant code.
-
Catch an exception to gather information or to report on an exception, and then propagate the exception.
To propagate an exception, rethrow the same exception or another exception in the catch block. An empty throw statement propagates the caught exception. Alternatively, throw a different exception.
Exceptions might propagate through several layers of an application. Ultimately, the exception could percolate to the user interface level. As an exception percolates, the exception becomes less specific. Exceptions from the lower echelon of an application contain detailed information appropriate to the application developer, but probably not relevant to the user. Internal exceptions might contain security and other sensitive information not appropriate for a benign (or malicious) user. Record the facts of the internal exception if logging is preferable. Exceptions that reach the user should present user-relevant information: a user-friendly message, steps to resolve the exception, or even a customer support link.
When an original exception is rethrown, you can preserve the that exception in the InnerException attribute. Successive InnerException attributes form a chain of exceptions from the current exception to the original exception. The InnerException can be initialized in the constructor of the new exception. Here is sample code that propagates an exception and sets the inner exception:
using System;
namespace Examples.Exceptions
{
public class Program
{
public static void Main()
{
try
{
MethodA();
}
catch (Exception except)
{
Console.WriteLine(except.Message);
}
}
public static void MethodA()
{
try
{
MethodB();
}
catch (DivideByZeroException inner)
{
// record divide by zero exception in event log.
// except is inner exception
throw new Exception("Math exception", inner);
}
}
public static void MethodB()
{
int var1 = 5, var2 = 0;
var1 /= var2;
}
}
}
C# .NET Events : Raising (Custom) Events
At the discretion of a publisher, an event is raised and the methods of the subscribers are called back. Raise an event with the call operator “()”. Adding the call operator to the event raises the event. Because a delegate underlies an event, the Invoke method inherited from a multicast delegate is another means to raising an event. The signature of both the call operator and the Invoke method match the delegate of the event. Any parameters are passed to the called methods of the subscribers. If an event returns a value as defined by the event delegate, the function of the last subscriber sets the return. Do not raise an event for events without subscribers. Events with no subscribers are null, which can be tested. An application that raises an event that has no subscribers will incur an exception because of a null reference. This is the proper way to raise an event:
public void SomeMethod()
{
if (anEvent != null)
{
anEvent(null, null);
}
}
EventArgs
Events sometime provide subscribers with custom information pertaining to the event. The mouse click event provides the x and y coordinates of the mouse pointer in the MouseEventArgs class. The DataRowChangeEventArgs class provides the affected row and action of several database-related events, such as the RowChanged event. The PaintEventArgs class is instantiated in a paint event. It gives developers the clip rectangle and graphics object for the paint event. Custom information about an event is defined in an EventArgs derived class. MouseEventArgs, DataRowChangeEventArgs, PaintEventArgs, and other related classes derive from the EventArgs class. These classes represent the state information for the event.
The EventArgs derived class that contains the state information of the event is typically passed as the second parameter of the event. State information can also be specified in the other parameters of events.
This is code for a bank account. The NSF event is raised when the account is overdrawn. The BankEventArgs class provides the bank account balance and amount of the transaction that would overdraw the account.
using System;
namespace Examples.Events
{
public class Program
{
public static void Main()
{
Bank account = new Bank();
account.NSF += NSFHandler;
account.Deposit(500);
account.Withdrawal(750);
}
public static void NSFHandler(object o,
BankEventArgs e)
{
Console.WriteLine("NSF Transaction");
Console.WriteLine("Balance: {0}", e.Balance);
Console.WriteLine("Transaction: {0}", e.Transaction);
}
}
public delegate void OverDrawn(object o, BankEventArgs e);
public class Bank
{
public event OverDrawn NSF; // non sufficient funds
public decimal Deposit(decimal amountDeposit)
{
propBalance += amountDeposit;
return propBalance;
}
public decimal Withdrawal(decimal amountWithdrawn)
{
decimal newBalance = propBalance - amountWithdrawn;
if (newBalance < 0)
{
if (NSF != null)
{
BankEventArgs args = new BankEventArgs(Balance, amountWithdrawn);
NSF(this, args);
}
return propBalance;
}
return propBalance = newBalance;
}
private decimal propBalance = 0;
public decimal Balance
{
get
{
return propBalance;
}
}
}
public class BankEventArgs : EventArgs
{
public BankEventArgs(decimal amountBalance,
decimal amountTransaction)
{
propBalance = amountBalance;
propTransaction = amountTransaction;
}
private decimal propBalance;
public decimal Balance
{
get
{
return propBalance;
}
}
private decimal propTransaction;
public decimal Transaction
{
get
{
return propTransaction;
}
}
}
}
-
Archives
- February 2009 (1)
- November 2008 (6)
- October 2008 (4)
- September 2008 (13)
- August 2008 (11)
- July 2008 (29)
- June 2008 (19)
- May 2008 (8)
-
Categories
-
RSS
Entries RSS
Comments RSS

