Programming Microsoft Visual Basic .NET for Microsoft Access Databases (Pro Developer)

Structured exception handling, or SEH, is the new kid on the block for run-time error processing. Although using SEH requires you to learn a new style of error processing, it offers several advantages. My personal favorite is that it gets you away from using the On Error GoTo statement, which leads to spaghetti code that can be difficult to debug. Many developers prefer not to dwell on run- time error processing, but the reality is that any professional program is likely to require competent processing of run-time errors. SEH is now the best way to do this.

Overview

Exceptions are the Visual Basic .NET way of referring to run-time errors. Because run-time errors frequently stem from faulty user input or other kinds of environmental errors, all applications are likely to have them. This is simply because you cannot force users of your applications to do the right thing. Therefore, you just have to live with the possibility of run-time errors occurring in your applications.

Living with exceptions is not the same as ignoring them. An unhandled exception can cause your application to relinquish control to the common language runtime (CLR). To recover from exceptions and still retain control so that users can continue using your application without having to restart it, you need to intercept exceptions before the CLR starts to process them. Visual Basic .NET offers two approaches for handling errors: unstructured exception handling and structured exception handling. Unstructured exception handling is based on the familiar On Error GoTo statement. Until now, Visual Basic and VBA developers knew this approach as their only option for processing run-time errors. As mentioned a moment ago, the second approach to handling exceptions ”SEH ”is new with Visual Basic .NET.

SEH initially gained popularity with C++ and Java, but Microsoft further enhanced this style of error processing with the introduction of the .NET Framework. All managed code has a uniform error-reporting format. This is because all run-time errors for managed code have to emanate from .NET Framework classes and the CLR. (That s why we call it managed code .) In earlier versions of Visual Basic and VBA, the style for reporting an error could vary by the source (such as Visual Basic), the data access tool used (Data Access Objects, Remote Data Objects, or ActiveX Data Objects), and the database engine. In the .NET Framework, exceptions are classes. A base class exception exists, and you can inherit it and build your own custom exceptions. You can throw your own run-time errors as easily as instantiating a new instance of the Exception class.

SEH offers a strategic advantage over unstructured exception handling. As mentioned, SEH avoids the introduction of spaghetti code associated with the On Error GoTo statement used with unstructured exception handling. A Try block ( Try End Try ) marks a block of code for exception handling with SEH. Every Try block must contain at least one Catch clause or a Finally clause. The Catch clause allows you to catch a type of error. You can specify particular types of exceptions to catch a narrow range of run-time errors, or you can use the System.Exception class to catch any type of run-time error.

In addition, an optional When clause is available to further refine the criteria for invoking the code for a Catch clause. You can include multiple Catch clauses within a single Try block. As soon as the .NET Framework detects an error matching a Catch clause, the code for that clause executes. In this way, Catch clauses are similar to Case clauses in a Select Case statement. The syntax for Try blocks even allows you to nest blocks within one another, but you might want to use single-level blocks as you introduce Try blocks into your coding tools. The optional Finally clause for each Try block executes whether or not an error occurs in the block. This clause can be especially useful for closing resources, such as database connections or files.

The general syntax for a Try block follows . The design shows a single- level Try block with two Catch clauses. The second clause illustrates the use of the optional When clause to further refine the criteria for selecting an individual Catch clause. You can have, at most, one Finally clause per Try block. The code in the Finally clause executes for every pass through a Try block.

Try Code to do something for which you want to check for exceptions Catch expression1 Code to handle the first type of exception Catch expression2 When other_expression2 Code to handle the second type of exception Finally Code to execute whether or not an exception occurs End Try

Setting Up the SEH Sample

The form for all the SEH samples appears in Figure 4-10. Because user input is a common source of exceptions, this SEH sample includes three text boxes for collecting user input to the application s code. The figure displays the form immediately after launching the sample. The text boxes have Name properties of TextBox1 , TextBox2 , and TextBox3 , from top to bottom. The buttons follow the same convention, from Button1 through Button5 . This form resides in the SEHSamples project and has five buttons for launching different approaches to programming SEH.

Figure 4-10: The SEH sample, which includes three text boxes for collecting user input to the application s code

The text boxes on the form allow the run-time specification of the beginning, ending, and step values for a For Next loop. The sample uses this loop as a source for exceptions. To cut down on your needs for inputting values to the text boxes, the Form1_Load event procedure primes the text boxes with some values. The following listing shows the values. Notice that the ending value is set to the maximum integer value with the MaxValue method for the Integer class. (Isn t it cool that data types are classes?) Visual Basic .NET automatically converts this to a string for the Text property of TextBox2 . The beginning value is just one less than the ending value.

Private Sub Form1_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load Initialize For...Next loop parameters. TextBox1.Text = Integer.MaxValue - 1 TextBox2.Text = Integer.MaxValue TextBox3.Text = 1 End Sub

SEH Programming vs. Programming Without Error Trapping

This section contrasts running an application with a basic form of SEH programming with running an application that has no exception handling. You can use this sample as a springboard for understanding subsequent samples that are more complex as well as a basis for appreciating what value SEH programming can add to an application.

Demonstrating an Exception Without a Catch

The purpose of the samples in this section is to demonstrate the generation (and handling) of exceptions. To that end, the Click event procedure for Button1 shows a For...Next loop that ends in an overflow error, which is a System.OverflowException object in Visual Basic .NET. When you run this procedure with the default values in the text boxes that the Form1_Load event places in the boxes, the code generates an error at the beginning of the third pass through the loop. The procedure will not normally make a third pass through the loop, but the code must add 1 to the int1 value when int1 is already at the maximum Integer value (2147483647). The procedure does this to determine whether to make a third pass through the loop. In adding 1 to the maximum Integer value, the procedure generates an overflow.

Because no catch for an exception within the procedure exists, the CLR handles the exception at run time. When the CLR catches the exception, it presents a dialog box describing that it detected an unhandled exception. A user can click one of three buttons to close the dialog box. One of these buttons allows a user to return to the application at a point before the onset of the error. Note that your application loses control of the session when the CLR presents the dialog box for an unhandled exception. In addition to returning to the application from the unhandled exception dialog box, the user can click a button to get more information about the error or even quit so that control does not return to the program.

Private Sub Button1_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Button1.Click When there is no exception handling, exceptions pass control to the CLR. Dim intBegin As Integer = CInt(TextBox1.Text) Dim intEnd As Integer = CInt(TextBox2.Text) Dim intStep As Integer = CInt(TextBox3.Text) Dim int1 As Integer Try to perform a For...Next loop based on values in text boxes. For int1 = intBegin To intEnd Step intStep Debug.WriteLine(int1.ToString) Next End Sub

To run the SEHSamples project and demonstrate the error as it appears normally, you need to start the program outside the Visual Studio .NET debugger. (Errors found with the debugger do not present the same unhandled exception dialog box as those found without the debugger.) You can do this by choosing the Windows Start button and then clicking Run. In the Run dialog box, you can type or navigate to the path and program executable for starting the project, such as C:\pawvbnet\SEHSamples\bin\SEHSamples.exe. Then, click OK on the Run dialog box. This opens the project outside of the debugger. Click Button1 to generate the dialog box reporting the unhandled exception. (See Figure 4-11.)

Figure 4-11: Dialog box for unhandled exceptions presented by the CLR for a System.OverflowException object

Catching an Exception

The event procedure for Button2 demonstrates the syntax for a very simple Try block that allows the application to retain control of the user s environment even after the exception. The Try block starts just before the beginning of the For...Next loop, and the block ends after the Catch clause inside the block. The block contains only one Catch clause, and it starts immediately after the For Next loop. This arrangement tests the For Next loop for exceptions and offers one remedy for any kind of error. The remedy is the presentation of a message box that denotes the type of message and description (or message) for the exception.

Private Sub Button2_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Button2.Click Exception handling returns built-in type and message; catch just one type of error. Dim intBegin As Integer = CInt(TextBox1.Text) Dim intEnd As Integer = CInt(TextBox2.Text) Dim intStep As Integer = CInt(TextBox3.Text) Dim int1 As Integer Try Try to perform a For...Next loop based on values in text boxes. For int1 = intBegin To intEnd Step intStep Debug.WriteLine(int1.ToString) Next Catch er As System.Exception MessageBox.Show( _ "Type = " & _ er.GetType.ToString() & vbCr & _ "Message = " & _ er.Message) End Try End Sub

As an Access database developer creating a solution for a client, it is good practice to prepare custom messages for exceptions that provide the maximum amount of helpful information to users. Microsoft has to write generic messages because its software will be used in a wide variety of situations. However, you can prepare custom messages that add value to a solution by helping users to rectify a problem.

You will notice that I did not add any special message in this situation. That s because the sample code for the Button2 Click event procedure has another point. When you catch an exception, the code in the Catch clause runs instead of the CLR code for presenting a dialog box in response to an unhandled exception. This means your application retains control of the user s environment when an exception occurs. In this sample, by clicking OK in the message box presented from the Catch clause (shown in Figure 4-12), the user can return to the application to change the values in the text boxes or to click another button. By handling the exception with a Try block, your application never relinquishes control of the user s environment. This is not true for an unhandled exception.

Figure 4-12: Handled exceptions enable your application to remain in control and determine user feedback.

Catching Multiple Exceptions in a Try Block

The Click event for Button3 demonstrates how to catch different kinds of errors. In addition, this event shows an example of a helpful message that suggests a remedy to the user. The preceding sample merely echoed the built-in error type and message. This sample divides errors into two types: those that fit a suggested remedy and those that do not. The procedure checks for these two types of errors with two Catch clauses inside the Try block.

The first Catch clause tests whether to suggest the remedy. The remedy is to reduce the ending value for the For Next loop by one step value. This remedy applies when an overflow exists and the int1 value in the loop is already at the maximum Integer value. The first Catch clause demonstrates the syntax for using the When clause inside a Catch clause. The first part of the Catch expression checks for a System.OverflowException object. If that s the kind of exception object that occurs, the first Catch clause uses the When clause to determine whether int1 equals the maximum Integer value. If both conditions are True , the first Catch clause suggests the remedy in a message box.

Any other kind of error invokes the second Catch clause. Without this general type of Catch clause, you can have a Try block that lets unhandled exceptions bubble up from the code in the Try block. Always place this kind of Catch clause as the last one in a Try block. This clause tests for any kind of Exception object and prints out a general message about it. Notice that the Err object that you used in classic Visual Basic is still available in Visual Basic .NET.

Private Sub Button3_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Button3.Click Exception handling returns custom error message and demonstrates an "all else" exception catch. Dim intBegin As Integer = CInt(TextBox1.Text) Dim intEnd As Integer = CInt(TextBox2.Text) Dim intStep As Integer = CInt(TextBox3.Text) Dim int1 As Integer Try Try to perform a For...Next loop based on values in text boxes. For int1 = intBegin To intEnd Step intStep Debug.WriteLine(int1.ToString) Next Catch er As System.OverflowException _ When int1 = Integer.MaxValue MessageBox.Show _ ("Upper bound exceeds maximum Integer " & _ "value. Reduce the Ending value by " & _ intStep.ToString & ".") Catch er As System.Exception MessageBox.Show("Time for a call to your system " & _ "pro." & vbCr & "Error description: " & _ Err.Description & vbCr & _ "Source for error: " & er.Source & ".") End Try End Sub

You can demonstrate the operation of the Button3_Click event procedure by opening a fresh instance of the application. Clicking F5 in Visual Studio .NET is sufficient in this case. Change the beginning value to 2147483643 (which merely involves updating the last digit to 3) and the step value to 2. Clicking Button3 with these text box values causes a message box to appear, similar to the one in Figure 4-13. This message states the remedy ”namely, reduce the ending value by 2. Make the change recommended in the message to confirm that the procedure comes to a normal end (no error message exists) with the recommended text box value settings.

Figure 4-13: Displaying in your application dynamic exception messages that tell the user exactly what to do

Now, modify the step value from 2 to 5 and click Button3. This causes the overflow to occur. The value for int1 is less than the maximum Integer value, so the first Catch clause does not apply. Therefore, the second Catch clause handles the exception.

Using the Finally Clause

The discussion of the preceding samples on exception processing dwelled on exceptions. However, with any luck at all, your programs will come to a normal end most of the time. It would be helpful if we received a final message that told us the outcome. (For example, The program came to a normal end. ) The preceding samples gave you feedback only if something went wrong. You can use the Finally clause in a Try block to provide feedback for any kind of outcome.

The next sample, Button4_Click , illustrates the use of the Finally clause to provide one of three types of feedback. As you know, the Finally clause executes whether or not the Try block detects an exception. First, the Finally clause can confirm that the program runs to its normal end. Second, the Finally clause can confirm that an error happened but that the application tried to fix it automatically. Third, the application can confirm that an error happened but that the procedure didn t attempt to fix the exception automatically.

This sample includes three Catch clauses. The first detects overflows where the step value is 1. In this case, the loop values are legitimate . However, a test exists for a loop that will not execute. This test generates an overflow. Therefore, the application automatically fixes this exception by passing beginning, ending, and step values to another procedure that performs the loop by using Decimal instead of Integer data types. The sample does not print an error message in this path unless the attempt to fix the problem fails. The second Catch clause catches any overflow associated with the For Next loop for any other purpose, such as for a loop with beginning, ending, and step values of 2147483643, 2147483646, and 5, respectively. With these values, the first attempt to pass through the For Next loop generates a System.OverflowException object. The third Catch clause collects any other exception that the Try block detects.

A couple of Boolean variables help the Finally clause determine which of its three closing messages to display. First, if the bolNoException variable is True , the Finally clause confirms a normal end. If the bolNoException variable is False and the bolFixup variable is True , the Finally clause confirms an automatic fixup attempt. If an error occurs during the attempt, a separate message box conveys that result. If the bolNoException variable and the bolFixup variable are both False , the Finally clause presents a message that an exception occurred but that no action was taken to fix the exception automatically. Again, a separate message box from a Catch clause provides more specific feedback.

Private Sub Button4_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Button4.Click Exception handling for multiple error types with automatic fixup try; demonstrates Finally clause syntax. Dim intBegin As Integer = CInt(TextBox1.Text) Dim intEnd As Integer = CInt(TextBox2.Text) Dim intStep As Integer = CInt(TextBox3.Text) Dim int1 As Integer Dim bolNoException As Boolean Dim bolFixup As Boolean Try Try to perform a For...Next loop based on values in text boxes. For int1 = intBegin To intEnd Step intStep Debug.WriteLine(int1.ToString) Next bolNoException = True Catch er As System.OverflowException When intStep = 1 IntToDecFixUP(int1, intBegin, intEnd, intStep) bolFixup = True Catch er As System.OverflowException When intStep > 1 Dim int2 As Integer = _ CDec(int1) + CDec(intStep) - Integer.MaxValue MessageBox.Show("Last iteration exceeds maximum " & _ "Integer value by " & int2.ToString & " with " & _ "a step value of " & intStep.ToString & ". " & _ "Reset beginning, ending, or step value to " & _ "avoid this result.") Catch er As System.Exception MessageBox.Show("Time for a call to your system " & _ "pro." & vbCr & "Error description: " & _ Err.Description & vbCr & _ "Source for error: " & er.Source & ".") Finally If bolNoException Then MessageBox.Show("Program ran to normal end.") ElseIf bolFixup Then MessageBox.Show("Exception happened, " & _ "but fix up tried.") Else MessageBox.Show("Exception happened, " & _ "but no fix up tried.") End If End Try End Sub Sub IntToDecFixUP(ByVal int1 As Integer, _ ByVal intBegin As Integer, _ ByVal IntEnd As Integer, _ ByVal IntStep As Integer) Automatic fixup routine Dim Myint1 As Decimal = CDec(int1) Myint1 = intBegin Try Do While Myint1 <= IntEnd Debug.WriteLine(Myint1.ToString) Myint1 += IntStep Loop Catch er As System.Exception MessageBox.Show("Automatic fixup failed") End Try End Sub

You can generate the three different messages from the sample by performing the following steps. Start the application (for example, by pressing F5). With the default text box values, click Button4 on Form1. This generates the message about the automatic fixup. No intermediate messages appear on the way to this message from the Finally clause. Next, set the beginning and ending values in the text boxes to 2147483643 and 2147483646; you can change only the last digit of the default values to set these values. Clicking Button4 with these values causes the Finally clause to display a message box confirming that a normal end occurred. Then, change the value in the step value text box to 5. (Leave the other two text boxes with the values that generated the message about a normal end.) Clicking Button4 generates a message about an error but says that no automatic fixup was tried. Before this message appears from the Finally clause, another message appears from the Catch clause with instructions on how to alter the text box values to remove the exception.

Throwing Your Own Exceptions

Developers might find it convenient to set traps for errors at run time that are awkward or difficult to generate manually. Throwing exceptions programmatically is an efficient way to test the Catch clauses in a Try block. The Click event for Button5 that follows illustrates how to throw exceptions programmatically. Button5_Click is an adaptation of Button3_Click , which we discussed earlier. Button3_Click was the first sample to demonstrate the syntax for two Catch clauses in a single Try block. The expression for the first Catch clause is purposefully narrow to make it easy to demonstrate the applicability of the second Catch clause. However, it might be difficult to create an error condition for a clause. (This is especially true when you are working with a general exception and are not certain what might throw it, which is different than saying you do not believe the exception will be thrown in the production version of an application.)

You need to understand that exceptions are objects. The following sample throws a general exception ( Exception object). This is the base class for all the more specific exceptions in the .NET Framework. You can make available the full list of Exception classes from the Debug, Exceptions menu command in the IDE. The resulting Exceptions dialog box presents a TreeView control that lets you browse for Exception classes, similar to the way that you can browse for files in Windows Explorer. To find the System.OverflowException class, open the Exceptions dialog box with the Debug, Exceptions command. Then, open the Common Language Runtime Exceptions and System branches of the TreeView control. Next, scroll down within the System branch to the System.OverflowException node. You can use this dialog box to view any exception. Once you have the name of the exception, you can create an instance of it.

The Button5_Click procedure is a replica of the Button3_Click procedure, except for the addition of a statement that throws the Exception class to create an instance of it. The statement appears after the For...Next loop and before the first Catch clause. Therefore, the Catch clauses can detect the thrown exception. The first Catch clause ignores the exception because this clause filters exclusively for System.OverflowException object instances. However, the second Catch clause can detect an Exception class instance thrown by the statement after the For Next loop. Because the sample creates an instance of the class whenever the For Next loop executes normally, all you have to do to throw the exception is set the text box values so that the For Next loop terminates successfully.

Private Sub Button5_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Button5.Click Throwing your own exception Dim intBegin As Integer = CInt(TextBox1.Text) Dim intEnd As Integer = CInt(TextBox2.Text) Dim intStep As Integer = CInt(TextBox3.Text) Dim int1 As Integer Try Try to perform a For...Next loop based on values in text boxes. For int1 = intBegin To intEnd Step intStep Debug.WriteLine(int1.ToString) Next Throwing my test error Throw New Exception("My Unresolved test error.") Catch er As System.OverflowException _ When int1 = Integer.MaxValue MessageBox.Show _ ("Upper bound exceeds maximum Integer " & _ "value. Reduce the Ending value by " & _ intStep.ToString & ".") Catch er As System.Exception MessageBox.Show("Time for a call to to system " & _ "pro." & vbCr & "Error description: " & _ Err.Description & vbCr & _ "Source for error: " & er.Source & ".") End Try End Sub

To test the procedure, open the application by pressing F5. Then, change the last digit for the ending value so that it is one number less than its default value. Next, click Button5. This causes the For Next loop to terminate normally. After the loop completes, control transfers to the Throw statement. This statement creates an Exception class object. The statement provides a message for the new Exception instance ( My Unresolved test error ). The second Catch statement detects this exception and displays the Message property as Err.Description . (See Figure 4-14.)

Figure 4-14: Creating an Exception instance and then catching it with a Catch clause in a Try block

 

Категории