Deitel & Associates, Inc. Logo

Back to
digg.png delicious.png blinkit.png furl.png
Visual Basic 2005
How to Program, 3/e

© 2005
pages: ~1500

Amazon logo

In Part 1 of this tutorial, we showed the premature termination of a program in which unhandled exceptions occur. In this tutorial, we demonstrate how to catch and handle these exceptions to enable the program to continue executing. This tutorial is intended for students and professionals who are familiar with classes and basic inheritance concepts in Visual Basic .NET or Visual Basic 2005.

Download the code examples for this tutorial here.

[Note: This three-part tutorial is an excerpt (Sections 12.3-4) of Chapter 12, Exception Handling, from our forthcoming textbook Visual Basic 2005 How to Program, 3/e. This tutorial may refer to other chapters or sections of the book that are not included here. Permission Information: Deitel, Harvey M. and Paul J., Visual Basic 2005 How to Program, ©2005. Electronically reproduced by permission of Pearson Education, Inc., Upper Saddle River, New Jersey.]

12.4 Example:Handling DivideByZeroExceptions and FormatExceptions

Let us consider a simple example of exception handling. The application in Fig. 12.2 uses exception handling to process any DivideByZeroExceptions and FormatExceptions that might arise. The application displays two TextBoxes in which the user can type integers. When the user presses Click To Divide, the program invokes event handler btnDivide_Click (lines 6–33), which obtains the user's input, converts the input values to type Integer and divides the first number (numerator) by the second number (denominator). Assuming that the user provides integers as input and does not specify 0 as the denominator, btnDivide_Click displays the division result in lblOutput. However, if the user inputs a noninteger value or supplies 0 as the denominator, an exception occurs. This program demonstrates how to catch and handle (i.e., deal with) such exceptions—in this case, displaying an error message and allowing the user to enter another set of values.

Before we discuss the program's details in Sections 12.4.1–12.4.5, let's consider the sample output windows in Fig. 12.2. The window in Fig. 12.2(a) shows a successful calculation, in which the user enters the numerator 100 and the denominator 7. Note that the result (14) is an Integer, because integer division always yields an Integer result. The next two windows, Fig. 12.2(b and c), demonstrate the result of an attempt to divide by zero. In integer arithmetic, the CLR tests for division by zero and, if the denominator is zero, generates a DivideByZeroException. The program detects the exception and displays the error message dialog in Fig. 12.2(c) indicating the attempt to divide by zero. The last two output windows, Fig. 12.2(d) and Fig. 12.2(e), depict the result of inputting a non-Integer value—in this case, the user enters "hello" in the second TextBox, as shown in Fig. 12.2(d). When the user clicks Click To Divide, the program attempts to convert the input Strings into Integer values using method Convert.ToInt32 (lines 15–16). If an argument passed to Convert.ToInt32 cannot be converted to an Integer value, the method throws a FormatException. The program catches the exception and displays the error message dialog in Fig. 12.2(e) indicating that the user must enter two Integers.

Fig. 12.2 | FormatException and DivideByZeroException exception handlers.
   1   ' Fig. 12.2: DivideByZeroTest.vb
   2   ' Exception handlers for FormatException and DivideByZeroException.
   3   Public Class frmDivideByZeroTest
   4       ' obtain 2 integers from the user
   5       ' and divide numerator by denominator
   6      Private Sub btnDivide_Click(ByVal sender As System.Object, _
   7         ByVal e As System.EventArgs) Handles btnDivide.Click
   9         lblOutput.Text = "" ' clear Label lblOutput
   11          ' retrieve user input and calculate quotient
   12            Try
   13             ' Convert.ToInt32 generates FormatException
   14             ' if argument is not an integer
   15            Dim numerator As Integer = Convert.ToInt32(txtNumerator.Text)
   16            Dim denominator As Integer = Convert.ToInt32(txtDenominator.Text)
   18             ' division generates DivideByZeroException
   19             ' if denominator is 0
   20             Dim result As Integer = numerator \ denominator
   22             ' display result in lblOutput
   23            lblOutput.Text = result.ToString()
   24         Catch formatExceptionParameter As FormatException
   25            MessageBox.Show("You must enter two integers.", _
   26               "Invalid Number Format", MessageBoxButtons.OK, _
   27                MessageBoxIcon.Error)
   28         Catch divideByZeroExceptionParameter As DivideByZeroException
   29            MessageBox.Show(divideByZeroExceptionParameter.Message, _
   30               "Attempted to Divide by Zero", MessageBoxButtons.OK, _
   31               MessageBoxIcon.Error)
   32         End Try
   33      End Sub ' btnDivide_Click
   34    End Class ' frmDivideByZeroTest

12.4.1 Enclosing Code in a Try Block

Now we consider the user interactions and flow of control that yield the results shown in the sample output windows. The user inputs values into the TextBoxes that represent the numerator and denominator, then presses Click To Divide. At this point, the program invokes method btnDivide_Click. Line 9 assigns the empty string to lblOutput to clear any prior result in preparation for a new calculation. Lines 12–23 define a Try block enclosing the code that might throw exceptions, as well as the code that is skipped when an exception occurs. For example, the program should not display a new result in lblOutput (line 23) unless the calculation in line 20 completes successfully.

The two statements that read the Integers from the TextBoxes (lines 15–16) call method Convert.ToInt32 to convert Strings to Integer values. This method throws a FormatException if it cannot convert its String argument to an Integer. If lines 15–16 convert the values properly (i.e., no exceptions occur), then line 20 divides the numerator by the denominator. If denominator is 0, line 20 causes the CLR to throw a DivideByZeroException. If line 20 does not cause an exception to be thrown, then the result is assigned to variable result and line 23 displays the result of the division.

12.4.2 Catching Exceptions

Exception-handling code appears in a Catch block. In general, when an exception occurs in a Try block, a corresponding Catch block catches the exception and handles it. The Try block in this example is followed by two Catch blocks—one that handles a FormatException (lines 24–27) and one that handles a DivideByZeroException (lines 28–31). A Catch block specifies an exception parameter representing the exception that the Catch block can handle. The Catch block can use the parameter's identifier (which is chosen by the programmer) to interact with a caught exception object. The type of the Catch's parameter is the type of the exception that the Catch block handles. Optionally, you can include a Catch block that does not specify an exception type or an identifier—such a Catch block catches all exception types. At least one Catch block or a Finally block (discussed in Section 12.6) must immediately follow a Try block.

In Fig. 12.2, the first Catch block (lines 23–27) catches FormatExceptions (thrown by method Convert.ToInt32), and the second Catch block (lines 28–31) catches DivideByZeroExceptions (thrown by the CLR). If an exception occurs, the program executes only the first matching Catch block. Both exception handlers in this example display an error message dialog. After either Catch block terminates, program control continues with the first statement after the last Catch block (the end of the method, in this example). We will soon take a deeper look at how this flow of control works in exception handling.

12.4.3 Uncaught Exceptions

An uncaught exception is an exception for which there is no matching Catch block. You saw the results of uncaught exceptions in the second and third outputs of Fig. 12.1. Recall that when exceptions occur in that example, the application terminates early (after displaying the exception's stack trace). The result of an uncaught exception depends on how you execute the program—Fig. 12.1 demonstrated the results of an uncaught exception when an application is executed in a Command Prompt. If you run the application from Visual Studio with debugging and the runtime environment detects an uncaught exception, the application pauses, and a window called the Exception Assistant appears indicating where the exception occurred, the type of the exception and links to helpful information on handling the exception. Figure 12.3 shows the Exception Assistant that is displayed if the user attempts to divide by zero in the application of Fig. 12.1.

12.4.4 Termination Model of Exception Handling

When a method called in a program or the CLR detects a problem, the method or the CLR throws an exception. Recall that the point in the program at which an exception occurs is called the throw point—this is an important location for debugging purposes (as we demonstrate in Section 12.7). If an exception occurs in a Try block (such as a FormatException being thrown as a result of the code in line 16 in Fig. 12.2), the Try block terminates immediately, and program control transfers to the first of the following Catch blocks in which the exception parameter's type matches the type of the thrown exception. In Fig. 12.2, the first Catch block catches FormatExceptions (which occur if input of an invalid type is entered); the second Catch block catches DivideByZeroExceptions (which occur if an attempt is made to divide by zero). After the exception is handled, program control does not return to the throw point because the Try block has expired (which also causes any of its local variables to go out of scope). Rather, control resumes after the last Catch block. This is known as the termination model of exception handling. [Note: Some languages use the resumption model of exception handling, in which after an exception is handled, control resumes just after the throw point.]

Fig. 12.3 | Exception Assistant.

Common Programming Error 12.1

Logic errors can occur if you assume that after an exception is handled, control will return to the first statement after the throw point.

If no exceptions occur in the Try block, the program of Fig. 12.2 successfully completes the Try block by ignoring the Catch blocks in lines 24–27 and 28–31, and passing over line 32. Then the program executes the first statement following the Try and Catch blocks. In this example, the program reaches the end of event handler btnDivide_Click (line 33), so the method terminates, and the program awaits the next user interaction.

The Try block and its corresponding Catch and Finally blocks together form a Try statement. It is important not to confuse the terms "Try block" and "Try statement"—the term "Try block" refers to the block of code following the keyword Try (but before any Catch or Finally blocks), while the term "Try statement" includes all the code from the opening Try keyword to the closing End Try. This includes the Try block, as well as any associated Catch blocks and Finally block, if there is one.

As with any other block of code, when a Try block terminates, local variables defined in the block go out of scope. If a Try block terminates due to an exception, the CLR searches for the first Catch block that can process the type of exception that occurred. The CLR locates the matching Catch by comparing the type of the thrown exception to each Catch's parameter type. A match occurs if the types are identical or if the thrown exception's type is a derived class of the Catch's parameter type. Once an exception is matched to a Catch block, the code in that block executes and the other Catch blocks in the Try statement are ignored.

12.4.5 Flow of Control When Exceptions Occur

In the sample output of Fig. 12.2(b), the user inputs 0 as the denominator. When the division in line 20 executes, a DivideByZeroException occurs. When the exception occurs, the Try block expires (terminates). Next, the CLR attempts to locate a matching Catch block. In this case, the first Catch block does not match—the exception type in the Catch-handler declaration is not the same as the type of the thrown exception, and FormatException is not a base class of DivideByZeroException. Therefore the program continues to search for a matching Catch block, which it finds in line 28. Line 29 displays the value of property Message of class Exception, which contains the error message. Note that our program never "sets" this error message attribute. This is done by the CLR when it creates the exception object.

In the sample output of Fig. 12.2(d), the user inputs hello as the denominator. When line 16 executes, Convert.ToInt32 cannot convert this String to an Integer, so Convert.ToInt32 throws a FormatException object to indicate that the method was unable to convert the String to an Integer. Once again, the Try block terminates, and the program attempts to locate a matching Catch block. A match occurs with the Catch block in line 24, so the exception handler executes and the program ignores all other exception handlers following the Try block.

Common Programming Error 12.2

Specifying a comma-separated list of parameters in a Catch block is a syntax error. A Catch block can have at most one parameter.

Other Exception Handling Tutorials:
Example: Divide by Zero Without Exception Handling
Example:Handling DivideByZeroExceptions and FormatExceptions (You are here).

Return to Tutorial Index