Building a Robust Application

There are two approaches to error handling. The first is to let the error occur, then do something about it; the second is to prevent the error from occurring. Each has its own merits. Supporters of the first approach argue that most errors occur very infrequently. For example, users of a computer system are by and large well trained and make few data entry errors. Therefore, the extra processing to prevali-date such things as numeric values is a waste of processing time and power; it's more efficient to execute an error-handling routine occasionally than to invoke a validation routine for every entry. On the other hand, supporters of the second approach argue that it's not good practice to allow errors to be generated and that if there are techniques available that prevent an error from occurring, they should be used. A remote server that's offline, for example, could take several minutes to generate an error due to network timeouts, whereas a correctly written validation function could report this in seconds. Furthermore, error-handling routines themselves can be the source of further errors.

I think that each argument has merits. You have to judge for yourself whether to use a "belts and braces" approach within a given procedure or whether the likelihood of an error is so remote that a simple error-handling routine will suffice. Don't forget that adding functionality to a program costs time, which is money. Here, though, is a list of simple error prevention measures you can add to any application without ballooning the development time:

Check data types

In situations where you accept a numeric value from the user to use within a vital function (like the index of an array, for example), you should first check that a numeric item has been entered using the IsNumeric function, and then check that it's within bounds.

Check that objects have been created

Before you rush into using an object variable, first check that an object reference has actually been assigned to it. If not, the object variable will contain a special value, Nothing. However, you can't use the equality operator (=) when querying an object variable; instead, you need to use the Is operator. For example, the following code checks that the object variable myObject isn't Nothing—i.e., that it is something!

If Not myObject Is Nothing Then ... continue what you where doing Else

92 Chapter 6- Error Handling

Trap Null data

When you are assigning column values from a recordset, you should ensure that the column value doesn't contain Null. This is quite a common occurrence with database recordsets, and often catches the unwary developer by surprise. Even if you don't expect to have any Null values, there may be occasions when one is recorded in the database. The next time you access that record, your program screams out with an "Invalid Use of Null" error.

There are two quick solutions for this. First, you can test the value using the IsNull function, as follows:

If IsNull(rsRecordset!FieldName) Then

Second, you can catch Null string values using the Format function with no parameters other than the field value. This returns a zero-length string and is the quickest and easiest method:

sProperty = Format(rsRecordset!FieldName)

Check array boundaries

Applications are quicker and easier to maintain if you keep literal values and constants to a minimum by making use of functions. One prime example of this is the boundary function to determine the lower and upper boundaries of an array. Imagine a situation where you regularly iterate through an entire array in many different places within the program. You know that there are 10 elements in the array, and that there always have been, so you write the function thus:

cboList.AddItem myArray(i) Next i

One day, someone comes along and adds a new element to the array. You're now faced with the task of finding all the instances where the program references the maximum element of the array and increasing it by one. However, if you had written the code with future maintenance in mind, it would look like this:

For i = LBound(myArray) to UBound(myArray)

cboList.additem myArray(i) Next i

Test for the presence of a remote server

VB developers are increasingly in the front line of client-server application development, which means that sooner rather than later you'll be called on to reference a remote ActiveX component from your client application. Regardless of the connection between client machine and the remote server, there will also come a time (again, usually sooner rather than later!) when the connection between the two machines is broken. When this happens, your client application will sit twiddling its thumbs waiting for the operating system to connect to the server, eventually the operation will time out, and to make matters worse, the only error message displayed will indicate that the ActiveX component couldn't be created—which could be caused by any number of things. An added complication to this little problem is DCOM; as the client application developer, you may not know where the server component is

Building a Robust Application 93

going to execute (and even if you do when writing your application, this may change over time).

The solution is to add a reference to a system DLL, RacReg.DLL, that contains a function you can use to return information about the remote server component you are trying to reach. Armed with this information, you can use any number of quick and easy tests to determine if the remote server is online. This could be as complex as writing a C++ program to access Net functionality or ping the server, to something as simple as using the Dir function on the root of the server.

For more information on using the RacReg DLL, see the GetAutoServerSettings entry in Chapter 7.

Use functions instead of sub procedures

Apart from their use for event handlers, I have yet to see a convincing argument for having custom Sub procedures in Visual Basic. In contrast, the use of functions in place of subroutines can improve the stability of your application. To demonstrate, let's compare two versions of the same program, the first of which uses a simple subroutine. In this example, we are going to call a subroutine to open a disk file, then use the contents of the file:

Private Sub Command1_Click()

On Error GoTo Command1_Err

Dim iFile As Integer Dim sContents As String iFile = FreeFile

Call FileOpener(File1.filename, iFile) Get #iFile, , sContents Text1.Text = sContents

Exit Sub 'don't forget to exit or the error 'handler will be executed!


MsgBox Err.Description & vbCrLf & Err.Number End Sub

Private Sub FileOpener(sFileName As String, _

iFileNo As Integer)

On Error GoTo FileOpener_Err

Open sFileName For Input As #iFileNo

Exit Sub


94 Chapter 6- Error Handling

MsgBox Err.Description & vbCrLf & Err.Number End Sub

As you can see, if the attempt to open the file fails, an error is raised in the subroutine, and a message displayed to the user. However, the code following the call to FileOpener is still executed, causing another error.

Now look how much smoother the operation is when using a function that returns True if the file is successfully opened and False if an error occurs:

Private Sub Command1_Click()

On Error GoTo Command1_Err

Dim iFile As Integer Dim sContents As String iFile = FreeFile

If FileOpener(File1.filename, iFile) = True Then Get #iFile, , sContents Textl.Text = sContents End If

Exit Sub


MsgBox Err.Description & vbCrLf & Err.Number End Sub

Private Function FileOpener(sFileName As String, _

iFileNo As Integer) As Boolean

On Error GoTo FileOpener_Err

Open sFileName For Input As #iFileNo

FileOpener = True

Exit Function


MsgBox Err.Description & vbCrLf & Err.Number End Function

In this second version, should an error occur within the FileOpener function, the function returns its default value of False, and the code to access the file's contents isn't executed. There are numerous places within an application where this style of programming can make the code both easier to read and more stable in execution.

0 0

Post a comment