System.Exception

System.Exception is the base exception class. All exceptions in .NET are derived from System.Exception. System.SystemException is the base class for system exceptions raised by the CLR, such as System.Data.DataException or System.FormatException. System.SystemException is derived directly from System.Exception. System.SystemException does not refine System.Exception. However, it is an important marker that distinguishes between system and application exceptions, as demonstrated in the following code:

using System;

namespace Donis.CSharpBook {
    public class Starter {
        public static void Main() {
            try {
                int var1 = 5, var2 = 0;
                var1 /= var2;    // exception occurs
             }
             catch(Exception except) {
                if (except is SystemException) {
                    Console.WriteLine("Exception thrown by runtime");
                }
                else {
                    Console.WriteLine("Exception thrown by application");
                }
            }
        }
    }
}

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. The Exception2 constructor has a string parameter, which sets the user-friendly message of the exception. The Exception3 constructor sets the user-friendly message and the inner exception, which is the originating exception. Exception4 deserializes an exception raised remotely.

The Exception class has several other helpful methods. Table 12-1 lists the important methods of the class.

Table 12-1. Exception methods

Method

Result

GetBaseException

Returns the original exception in a chain of exception objects

GetObjectData

Serializes data of the Exception class

GetType

Returns the type of the exception

ToString

Returns a concatenation of the name of the exception object and 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 Donis.CSharpBook {
    public class Starter {
        public static void Main() {
            try {
                MethodA();
            }
            catch(Exception except) {
                Exception original = except.GetBaseException();
                Console.WriteLine(original.Message);
            }
        }

        public static void MethodA() {
            try {
                MethodB();
            }
            catch(Exception except) {
                throw new ApplicationException("Inner Exception", except);
            }
        }

        public static void MethodB() {
            throw new ApplicationException("Innermost Exception");
        }
    }
}

System.Exception Properties

System.Exception has a full complement of properties providing information on the exception. Table 12-2 describes the properties of the Exception class.

Table 12-2. Exception properties

Property

Description

Type

Read/Write

Data

Returns a dictionary collection that provides additional information pertaining to the exception.

IDictionary

R

HelpLink

Link to a help file describing 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 and available to derived types.

int

R/W

InnerException

When exceptions are propagated, the inner exception represents the previous exception. When the outer exception is thrown, the InnerException can be set.

Exception

R

Message

User-friendly message describing the exception.

string

R

Source

Name of application or object where the exception occurred.

string

R/W

StackTrace

String representation of the call stack when the exception occurred.

string

R

TargetSite

Reference to the method where the exception was 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 then caught and handled in Main. In the catch statement block, leveraging the TargetSite property, MethodA is then called again successfully. The TargetSite property returns a reference to an object of MethodBase type, which can be used to invoke the method where the exception occurred. At that time, the method is invoked anew with a fresh state. MethodBase.Invoke is explained fully in Chapter 10.

using System;
using System.Reflection;

namespace Donis.CSharpBook {
    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);
            }
        }

        public static void MethodA() {
            if (bException) {
                throw new ApplicationException("exception message");
            }
        }
    }
}

Application Exceptions

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 can also be used to brand a group of custom exceptions; when several custom exceptions are planned, create a custom base exception class to categorize the exceptions. For convenience and maintainability, deploy application exceptions as a group in a separate assembly.

Do not accidentally 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:

  1. Name the application exception. As a best practice, the class name should have the Exception suffix, as in DivideByZeroException.

  2. Derive the application exception from System.Exception.

  3. Define constructors that initialize the state of the application exception. This includes initializing members inherited from the base class.

  4. Within the application exception, refine System.Exception as desired, such as adding properties 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 reacts to software exceptions in the same way it does to standard exceptions. Here are some examples of throw syntax:

throw exceptioninstance;

throw;

The second syntax is specialized: It is available in a catch statement block but nowhere else. This version of the throw statement rethrows an exception caught in the catch statement block. However, the best policy is to add additional context to an exception before propagating the exception object. Propagating exceptions was reviewed earlier in this chapter.

Application exceptions are typically prompted by an exceptional event. What is an exceptional event? A strict definition for this term does not exist. Basically, you define the basis of an event. Remember that raising an exception simply for transfer of control or for a nonexceptional event is bad practice. In an application, the following could be considered exceptional events for which throwing an application exception is warranted:

  • Constructor fails to initialize the state of an object.

  • A property does not pass validation.

  • Some parameters should refer to an object. If the parameter is null, throw an exception.

  • An exceptional value is returned from a function.

  • A function that returns void has an exceptional event. Since the function return type is void, it cannot return an error code on an exceptional event. In this circumstance, throwing an exception is appropriate.

ConstructorException is an application exception. In the following sample code, this exception is thrown when a constructor fails. It refines the System.Exception base class by adding a name and time property. In addition, the Message property is assigned an appropriate message. This is the code for the ConstructorException class:

using System;

namespace Donis.CSharpBook {

    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 Donis.CSharpBook {
    public class Starter {
        public static void Main() {
            try {
                ZClass obj = new ZClass();
            }
            catch(ConstructorException except) {
                Console.WriteLine(except.Message);
                Console.WriteLine("Typename: " + except.Typename);
                Console.WriteLine("Occurred: " + except.Time);
            }
        }
    }

    class ZClass {
        public ZClass() {
            // initialization fails
            throw new ConstructorException(this);
        }
    }
}

Exception Translation

In some circumstances, the CLR catches an exception and rethrows a different exception. The inner exception of the new exception contains the original exception. Check documentation in the Framework Class Library (FCL) to confirm when exception translation occurs. For example, invoking a method dynamically using reflection is one such circumstance. Exceptions raised in methods invoked by MethodInfo.Invoke are automatically trapped and converted to TargetInvocationException. The following code produces an example of exception translation:

using System;
using System.Reflection;

namespace Donis.CSharpBook {

    public class ZClass {
        public static void MethodA() {
            Console.WriteLine("ZClass.MethodA");
            throw new Exception("MethodA exception");
        }
    }

    public class Starter {
        public static void Main() {
            try {
                Type zType = typeof(ZClass);
                MethodInfo method = zType.GetMethod("MethodA");
                method.Invoke(null, null);
            }
            catch(Exception except) {
                Console.WriteLine(except.Message);
                Console.WriteLine("original: " +
                    except.InnerException.Message);
            }
        }
    }
}

COM Interoperability Exceptions

.NET applications often host COM components or expose managed components to COM clients. These applications must be prepared to handle and possibly throw COM exceptions, respectively. The prevalence of COM components makes COM interoperability an important consideration for managed applications into the foreseeable future.

COM Exceptions

COM components should sandbox exceptions, which protects COM clients from potential language-specific or platform-specific exceptions. COM methods return an HRESULT structure, which is the result code of the method. An HRESULT is a 32-bit structure, where the severity bit is in the high-order bit. The severity bit is set if any exception is raised. The Win32 Software Development Kit (SDK) defines constants representing various HRESULT codes. E_NOINTERFACE, E_INVALIDARG, E_OUTOFMEMORY, S_OK, and S_FALSE are common HRESULT codes. E_XXX codes are error codes indicating that an exception was raised or some other exceptional event happened. S_XXX codes are success codes where no failure is reported.

When managed code calls methods on COM objects, the CLR consumes the resulting HRESULT. If the HRESULT represents a known COM error (an E_XXX code), the CLR maps the HRESULT to a managed exception. For example, E_POINTER maps to the NullReferenceException, which is a managed exception. An error code from an unknown HRESULT is mapped to a COMException object. No exception is thrown if the HRESULT is a success code (an S_XXX code). Table 12-3 shows the common translations of HRESULT to managed exceptions.

Table 12-3. COM exception table

COM Exception

Managed Exception

COR_E_OVERFLOW

OverflowException

COR_E_THREADSTOP

ThreadStopException

E_NOINTERFACE

InvalidCastException

E_NOTIMPL

NotImplementedException

E_OUTOFMEMORY

OutOfMemoryException

E_POINTER

NullReferenceException

The COMException is derived from System.Runtime.InteropServices.ExternalException, which indirectly derives from System.Exception. The COMException class has additional properties that hold the details of the unknown COM exception. For example, the ErrorCode property contains the HRESULT from the COM method.

COM components implement Error objects to provide extended error information to clients. An Error object implements the IErrorInfo interface. Members of the IErrorInfo interface correlate to members of the COMException class and are therefore accessible to the managed client. Table 12-4 maps members of the Error object to the COMException class.

Table 12-4. IErrorInfo to COMException mapping

IErrorInfo member

COMException member

IErrorInfo::GetDescription

COMException.Message

IErrorInfo::GetSource

COMException.Source

If IErrorInfo::GetHelpFile is non-zero, IErrorInfo::GetHelpFile+"#"+IErrorInfo::GetHelpContext

COMException.HelpLink

The following code is a partial listing from an Active Template Library (ATL) project that publishes a COM component. The COM component exposes the CComponentZ::MethodA. Using the AtlReportError API, CComponentZ::MethodA builds an Error object to return extended error information to the client. The method also returns a custom error code in HRESULT, which therefore will be unknown to the CLR:

// ComponentZ.cpp : Implementation of CComponentZ

#include "stdafx.h"
#include "ComponentZ.h"
#include ".componentz.h"

// CComponentZ

STDMETHODIMP CComponentZ::MethodA(void) {
    // TODO: Add your implementation code here

    HRESULT hResult = MAKE_HRESULT( 1, FACILITY_NULL, 12 );

    MessageBox(NULL, "COM component", "Hello from", MB_OK);
    return AtlReportError (GetObjectCLSID(), "My error message", 5,
        "http://error.asp",GUID_NULL, hResult);
}

The following code is managed code, in which the ATL component is called from a managed COM client. Because the HRESULT is unknown, the error code appears as a COMException exception. The code uses COMException properties to report additional information about the custom exception:

using System;
using System.Runtime.InteropServices;

namespace COMClient {
    class Program {
        static void Main(string[] args) {
            try {
                 COMLib.CComponentZClass com_object =
                     new COMLib.CComponentZClass();
                 com_object.MethodA();
            }
            catch (COMException except) {
                 Console.WriteLine(except.ErrorCode);
                 Console.WriteLine(except.HelpLink);
                 Console.WriteLine(except.Message);
            }
            catch (Exception) {

            }
        }
    }
}

Generating COM Exceptions

Managed exceptions have an HRESULT property that translates the exception to a COM error result. System exceptions are already assigned an appropriate HRESULT. For application exceptions, you should initialize the HRESULT property in the constructor. A managed component that expects COM clients must set the HRESULT for all exceptions.

The following code defines TypeException as an example of an application exception. TypeException should be thrown when an object is the wrong type. TypeException has two overloaded constructors. Both constructors set the HResult property of the exception to the E_NOTIMPL error code (0x80004001). The one-argument constructor accepts a type object, which indicates the required type that was not implemented. The name of the type is added to the error message of the exception:

using System;

namespace Donis.CSharpBook {

    public class TypeException: Exception {

        public TypeException()
                : base("object type wrong") {
            HResult = unchecked((int) 0x80004001); // E_NOTIMPL
        }

        public TypeException(Type objectType)
                : base("Argument type wrong: " + objectType.Name +
                    " required") {
            prop_RequiredType=objectType.Name;
            HResult = unchecked((int) 0x80004001); // E_NOTIMPL
        }

        private string prop_RequiredType;
        public string RequiredType {
            get {
                return prop_RequiredType;
            }
        }
    }
}

In the following code, the Delegator class uses the TypeException class. Delegator delegates method calls of Delegator.MethodA to an external object. For the delegation to be successful, the external object must also implement the MethodA method, which is defined in ZInterface. Appropriately, the code in Delegator.MethodA checks for the implementation of ZInterface. If ZInterface is not implemented, TypeException is thrown. Otherwise, Delegator.MethodA proceeds with the delegation:

using System;

namespace Donis.CSharpBook {
    interface ZInterface {
        void MethodA();
    }

    public class Delegator {
        public Delegator(object obj) {
            externalobject = obj;
        }

        public void MethodA() {
            if (externalobject is ZInterface) {
                ((ZInterface)externalobject).MethodA();
            }
            else {
                throw new TypeException(
                    typeof(ZInterface));
            }
        }

        private object externalobject;
    }
}

The YClass.UseDelegator method shown in the following code creates an instance of the Delegator class. A ZClass object is passed into the Delegator constructor as the external object. ZClass does not implement MethodA. Delegator.MethodA is called in YClass.UseDelegator. A TypeException is thrown because ZClass does not implement the appropriate interface:

using System;
using System.Runtime.InteropServices;
using Donis.CSharpBook;

class ZClass {
}

[ClassInterface(ClassInterfaceType.AutoDual)]
public class YClass {
    public void UseDelegator() {
        ZClass obj = new ZClass();
        Delegator del = new Delegator(obj);
        del.MethodA();
    }
}

COM clients can access managed code through COM Callable Wrappers (CCWs). The following unmanaged code creates an instance of YClass and invokes YClass.UseDelegator. As expected, an exception occurs, which translates to E_NOTIMPL in unmanaged code. The COM client checks for this exception and displays the appropriate message:

#import "..yclass.tlb" no_namespace, raw_interfaces_only, named_guids

#include "objbase.h"

void main() {
    CoInitialize(NULL);
    _YClassPtr obj(CLSID_YClass);
    HRESULT hResult = obj->UseDelegator();
    if (hResult == E_NOTIMPL) {
       MessageBox(NULL,"Required interface not implemented",
           "In Managed Component", MB_OK);
    }
    else {
        MessageBox(NULL, "Managed Component", "No error",
            MB_OK);
    }
}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.253.198