Writing Code to Handle Exceptions

function OpenWin(url, w, h) { if(!w) w = 400; if(!h) h = 300; window.open(url, "_new", "width=" + w + ",height=" + h + ",menubar=no,toobar=no,scrollbars=yes", true); } function Print() { window.focus(); if(window.print) { window.print(); window.setTimeout('window.close();',5000); } }
Team-Fly    

Special Edition Using Microsoft® Visual Basic® .NET
By Brian Siler, Jeff Spotts
Table of Contents
Chapter 26.  Debugging and Performance Tuning


Users of your program are not likely to be developers or Visual Studio users. Therefore, to make your application more robust, you'll want to add exception-handling code to control its behavior after an exception is thrown. Good exception-handling code should do the following:

  • Make sure the error is logged accurately, so you know which method call actually caused the exception.

  • Depending on the severity of the exception, you may want to display a message for the end user. This message should be written so that the user can understand it, and if necessary it should provide instructions on how to resolve the problem.

In a complex application, you may have objects that call other objects in a nested manner. It is not uncommon to have 10 procedures on the call stack when an exception occurs. A typical error-handling strategy for an application with lots of nested calls is to keep throwing the exception back to the calling function until finally it reaches the user interface. However, depending on the severity of the exception, you may or may not always want the user to see it. Microsoft also suggests rather than just "bubbling up" the same exception, that you throw a new exception that adds some information from the current subroutine. In this section, we'll describe how to pass exceptions around, keep track of them in the Windows Event log, and create custom exception types.

Understanding Finally Blocks

Your code and the associated exception handling code can be thought of as existing in blocks, or sections of code bounded by Try and End Try statements. As you learned in Chapter 5, a typical section of code using exception handling has a Try block and a one or more Catch blocks:

 Try      'CODE YOU WANT TO EXECUTE  Catch ex As Exception      'CODE TO HANDLE EXCEPTION  End Try 

The previous code sample is a minimal implementation of exception handling. During a normal errorless execution of the program, code in the Catch block does not execute. However, when an exception is thrown, execution jumps to the Catch block, then proceeds at the first statement following the End Try statement. Therefore, when an exception occurs, lines of code in the Try block may not be executed. In certain situations, you may need to perform cleanup activities whether or not an exception occurs. For example, suppose you need to copy one hundred files, perform some action, and then delete the files. If you place the code for all of these activities in the Try block, it is possible some files could be copied but never deleted. Fortunately, VB .NET provides the Finally block, which will always execute regardless of exceptions:

 Try      For i = 1 to 100          CreateTemporaryFile(i)      Next      ProcessTemporaryFiles()  Catch ex As Exception      Messagebox.Show("Error occurred!")  Finally      DeleteTemporaryFiles()  End Try 

In the previous code sample, the DeleteTemporaryFiles function will always be executed. Although you could also add code to delete the files in both the Try and Catch blocks, using a Finally block makes your code easier to read and avoids duplication.

Throwing Exceptions to the Calling Function

You throw exceptions in a program by executing the Throw method. You can throw an existing exception object, or create a new one and set its properties. It is also very common to declare and throw a new exception using a single line of code, as in the following example:

 Throw New Exception("Error Opening File!") 

The text in parentheses represents the message sent back to the calling program, which will receive a generic Exception object containing the message text. Of course, there are more specific types of exceptions (all inherited from the base class Exception), and you can also create your own custom exception types.

Taking Advantage of Exception Types

The advantage of catching a specific exception type is that they will often contain special properties that can be used to provide more detailed information about the exception. For example, the SQLException class has a Server property, which indicates the name of the server involved in the SQL-related exception:

 Try       cn.Open()  Catch sqlexc As SqlException       Throw New Exception("Error connecting to " & sqlexc.Server)  Catch otherexc As Exception       Throw New Exception("Error opening connection")  End Try 

In the previous sample code, we have written code to catch both a SQLException and a regular exception. If a SQLException occurs, we create an error message that contains the server name. Otherwise, we use a generic message indicating that something went wrong. In either case, a new exception is thrown back to the calling function.

Note

Catch statements are evaluated in order, so make sure to place specific exception types before more general ones.


Providing More Detailed Information to the Caller

In our previous example, the caller does not know that the password was invalid, only that an error occurred while connecting to a server. All the detailed information from the original SQLException was lost, because we declared a new Exception object with just a text message. An even better strategy is to send the original SQLExceptionto the caller via the InnerException property of the new exception object:

 Try       cn.Open()  Catch sqlexc As SqlException       Throw New Exception("Error connecting to " & sqlexc.Server, sqlexc)  Catch otherexc As Exception       Throw New Exception("Error opening connection", otherexc)  End Try 

Each exception has an InnerException property, which can be specified as a second parameter to the New constructor. In the preceding sample code, we send custom information back from the current function call, as well as the original exception that caused the problem. By following this strategy throughout your program, the exception at the top level will have detailed exception information that leads back through the nested calls to the actual problem:

 System.Exception: Error calling ExecSP ---> System.Exception: Error  Connecting to bserver ---> System.Data.SqlClient.SqlException: Login  failed for user 'sa'.     at System.Data.SqlClient.SqlConnection.Open() 

Using the ToString property of the exception caught two levels up from our sample code retrieved the previous text. As you can see, arrows are automatically added by Visual Studio to separate each function call. The detailed information is not lost when moving up the chain, because the original exception was passed using the InnerException property.

Using Custom Exceptions

The example of a connection error is a severe exception caused by something external to your program. However, you may also want to throw exceptions when errors occur that are related to the business logic of your application.

Note

When creating custom exceptions, it is important to understand the difference between an exception (something that shouldn't normally occur) and data validation (something that normally occurs when processing user input). For example, if you need to check something every time data is entered, a function call might be more appropriate than an exception.


Suppose you are processing invoices for payment using a multi-tier application. The user interface could be a Web page or Windows application. The heart of the application would be a business object that actually processes the invoices, residing on a server somewhere. Figure 26.4 shows an example of our fictitious user interface.

Figure 26.4. You can use exceptions to enforce business rules, such as a rule that detail amounts must add up to a total.

graphics/26fig04.gif

Note

A business object is just a Visual Basic class of your own creation that contains rules to support a business application.


Notice that in Figure 26.4 the user interface has a total amount field as well as line-item amount fields that provide a detailed breakdown of the total. Therefore, it would make sense that this application includes code to make sure the detailed amounts added together must match the total. This is known as a business rule. Although it's generally a good idea to keep all the business logic together in one place, a typical application would also include basic data validation logic within the UI for speed purposes. So, in theory, the business object should never receive an "out of balance" invoice from the user interface.

However, should the business object just assume that any invoice it receives is valid? Probably not, because another programmer may want to use the same business object in the future and leave out the validation from his UI. To prevent erroneous invoices from being processed, you could throw an exception in the business object. By creating a custom exception class, you could add specific properties that apply to the task at hand.

Creating a Custom Exception Class

Listing 26.1 shows the code for creating and throwing a custom exception.

Listing 26.1 Exception.ZIP Using a Custom Exception Class
 Public Class BusLogicException      Inherits Exception      Public UserMessage As String      Public TechnicalMessage As String  End Class  Public Class InvoiceProcessor     Public Sub ProcessInvoice(ByVal TotalAmount As Single,_  ByVal DetailAmounts() As Single)        Dim i As Integer        Dim sngDetailTotal As Single = 0        For i = 0 To UBound(DetailAmounts)           sngDetailTotal += DetailAmounts(i)        Next        'THROW A CUSTOM EXCEPTION IF NECESSARY        If sngDetailTotal <> TotalAmount Then           Dim BalanceEx As New BusLogicException()           BalanceEx.UserMessage = "Detail amounts do not balance with the total!"           BalanceEx.TechnicalMessage = " Total=" & TotalAmount & _           " InvalidTotal=" & sngDetailTotal           Throw BalanceEx        End If        Try           'PROCESS INVOICE HERE.        Catch exc As Exception           Throw New Exception("Error Processing Invoice.", exc)        End Try     End Sub  End Class 

As you can see from the listing, InvoiceProcessor is the name of our sample business object class, and BusLogicException is the name of a custom exception class. BusLogicException inherits from the Exceptionclass and adds two new properties: TechnicalMessage and UserMessage. The UserMessage property might contain information that would make sense to the user, whereas the TechnicalMessage property could contain a more detailed description used to assist help desk personnel. Users don't know what a SQL server is (and shouldn't have to), but they will understand a message like "network connection problems." Note that the code in Listing 26.1 also includes a Try ... Catch block for handling other exceptions besides the custom one, should they occur.

Note

For standard exceptions, the Message property of an exception contains just the error message. The output of the ToString method adds more detail, including a stack dump.


Note

Because our custom exception class inherits from the Exception base class, you can pass it as the InnerException when throwing a generic exception.


Catching a Custom Exception

Catching a custom exception is no different from any other exception. You can use a generic Exception object, or specify the custom type to access the exception's special properties:

 Dim objProcessor As New clsBusObject.InvoiceProcessor()  Try     Call objProcessor.ProcessInvoice(sngTotalAmount, sngDetailArray)     Messagebox.Show("Invoice Processed Successfully")  Catch blExc As clsBusObject.BusLogicException     Messagebox.Show(blExc.UserMessage)  Catch exc As Exception     Messagebox.Show("Unknown error: " & exc.Message)  End Try 

In this example, the exception-handling code displays the UserMessage property for the custom exception, or the Message property of any other exception.

Using the Windows Event Log

Within the Visual Studio environment, the call stack can help you locate the root cause of an exception. However, at runtime you may want to log errors for later analysis. Although errors can be logged to a text file or other custom location, the Windows Event log provides several advantages:

  • The Windows Event log provides a central area you can use to determine the health of a server in general.

  • Network administrators and other operational personnel may already be familiar with Microsoft's built-in Event Viewer utility, pictured in Figure 26.5.

    Figure 26.5. The Windows 2000 Event Viewer allows you to manage both standard and custom event logs.

    graphics/26fig05.gif

  • The Event Viewer allows you to check an event log remotely.

  • You can use Visual Basic code to access an event log programmatically and alert others if certain types of events occur.

Before we begin discussing the technical details of using an event log, we need to clarify a couple of terms in more detail:

  • An event entry is an event that happens on a computer and is entered in a log. Events are not always errors. For example, when your computer boots, several events are logged just for informational purposes. The Event Viewer provides icons that indicate the different types of events.

  • An event log contains a record of related events. For example, the Security Event log contains events related to system security. Windows ships with three built-in logs (Application, System, and Security), plus you can add additional logs as desired. All these logs can be managed using the Event Viewer.

  • An event source identifies the creator of the event log entry, such as the name of your application.

The System.Diagnostics namespace contains the classes necessary for manipulating Windows Event logs. In this section we will describe how to write and read these logs.

Writing Errors to the Event log

In order to write errors to an event log, you have to specify an event source name. An event source is just a string value, which is usually just the name of your application or business object, such as InvoiceProcessor. For every event source, Windows keeps track of the associated event log, which is by default the Application log. However, you can also put your events in a custom event log. To associate an event source with a custom event log, you have to use the CreateEventSource method but you only have to do it once. The computer remembers the log from that point forward. Actually writing the event information to the log is accomplished with the WriteEntry method, as shown in Listing 26.2.

Listing 26.2 EVENTLOG.ZIP Writing to an Event Log
 Public Sub LogException(ByVal FunctionName As String,_                          ByVal NewException As Exception)  Dim evtLog As New System.Diagnostics.EventLog()  If Not evtLog.SourceExists(FunctionName) Then     evtLog.CreateEventSource(FunctionName, "MyLog")  End If  evtLog.WriteEntry(FunctionName, NewException.ToString, EventLogEntryType.Error)  evtLog.Close()  End Sub 

The LogException function accepts a function name and exception object as arguments, and writes the exception to a custom event log called "MyLog." Notice the third parameter of the WriteEntry function, which determines the type of event. By adding the LogException function to your application, you can write errors to the event log when they occur with just a single line of code.

Note

Certain types of errors (such as an out-of-balance invoice that is easily corrected at data entry) you should not write to the log. However, ADO and SQL exceptions, or any other unknown errors should be logged, because you weren't expecting them.


A typical use of the LogException function would be to log an error during exception handling, as in the following example:

 Try     Call ProcessInvoice(sngAmount,arDetails)  Catch ex As Exception     Dim NewEx As New Exception("Error in ProcessInvoice", ex)     LogException("InvoiceApp", MyEX)     Throw MyEX  End Try 

Figure 26.6 shows what a custom exception looks like when written to the event log.

Figure 26.6. To display the event viewer in Windows 2000, right-click the My Computer icon and choose Manage.

graphics/26fig06.gif

Note

Many functions in the EventLog class also accept an optional parameter for a computer name, allowing you to write or read from remote logs. For more information, see the help files.


Monitoring an Event Log

In addition to writing events to a log, you can also read an event log with Visual Basic code. For example, you could have a program automatically check your logs for errors from a certain source application (that is, "Nightly Data Update Process") and e-mail an administrator to notify him of an error. All the event entries are stored in the Entries collection of an EventLog object, which you can navigate with a For loop. However, an even more useful hook into the event system is the EntryWritten event, which can notify your program when an event occurs. Listing 26.3 shows an example of an event handler that checks for the string PROBLEM in an event message.

Listing 26.3 EVENTLOG.ZIP Event Log Notification
 Dim evtTemp As System.Diagnostics.EventLog  Private Sub btnStart_Click(ByVal sender As System.Object,_  ByVal e As System.EventArgs) Handles btnStart.Click     evtTemp = New System.Diagnostics.EventLog("MyLog")     evtTemp.EnableRaisingEvents = True     AddHandler evtTemp.EntryWritten, AddressOf CheckEvent     btnStart.Enabled = False  End Sub  Private Sub CheckEvent(ByVal sender As Object,_  ByVal args As System.Diagnostics.EntryWrittenEventArgs)     If InStr(args.Entry.Message.ToUpper, "PROBLEM") <> 0 Then        Messagebox.Show("Houston, we have a problem!")     End If  End Sub 

The preceding code starts the notification process by setting the EnableRaisingEvents property to True and then passing the address of the CheckEvent function using the AddHandler method. From that point forward, the evtTemp object will call your function any time a new event is added. The event entry is passed to the function, allowing you to determine that the message or other properties meet the criteria necessary to alert an end user.


    Team-Fly    
    Top
     



    Special Edition Using Visual Basic. NET
    Special Edition Using Visual Basic.NET
    ISBN: 078972572X
    EAN: 2147483647
    Year: 2001
    Pages: 198

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net