11/9/2011 The Three Types of Errors Error Handling • Syntax error: an error in the form of your program that prevents it from running. Also called a compiler error in VBA • Run-time error: an error that arises at run time and makes your program terminate abnormally (crash) • Logic error: an error in which your program appears to run normally but produces an answer that is not correct Type 1: Syntax Errors Type 2: Logic Errors • These errors are caught by the VBA environment, which also attempts to suggest fixes • They can be very annoying, but since you can’t run your program with one of these errors in place, they are actually not that much of a problem once you have programmed a bit • The built-in Help can be very useful in diagnosing and correcting these • These are the most insidious, because your program appears to run normally • In fact, it does exactly what you told it to, only there is a logic error so what you told it to do is not actually what you wanted • The only defense is thorough testing. You need to develop a good set of tests BEFORE you start coding. You should also test complex spreadsheets even if there is no code involved The Infamous Disclaimer Why? • Have you ever read the fine print on the agreements you must authorize before you download software? • Most commercial software has at least a few “bugs”, including logic errors • The disclaimer says you can’t sue the company for damages if you relied on a faulty answer from their software • In the software business, the theory is that the first product of a given type to market grabs the big market share • So software firms may put their product out before it is thoroughly tested • Since this strategy has worked well in the past, it is hard to blame them too much, though it is unprofessional 1 11/9/2011 Type 3: Run-time Errors Testing for Errors • Run time errors happen when the program encounters an unexpected condition that it can’t handle • Division by zero, needing a file that’s not there, or user error entering a value, are common causes of runtime errors • A well-written program should recover gracefully from errors, ideally allowing the user another chance to enter their data, say, if that was the cause of the problem • In addition to your normal use cases, you should also develop cases for all the user mistakes or other error conditions you can think of • For each of these error cases, you should have one or more tests • Your program should recover gracefully in each case; if it crashes, you need to figure out a way to avoid this Code to Handle Errors (Example 1) Code to Handle Errors (Example 2) • We’ve already seen some error-handling examples in our code for reading from a file: '*** get the name of the file to use fName = Application.GetOpenFilename() If fName = False Then 'user cancelled Exit Sub End If On Error GoTo EndMacro: 'bail out if there is an error … EndMacro: On Error GoTo 0 'just quit if there's an error • This jumps out of the subroutine if the user leaves a blank entry or otherwise fails to enter a file name. Ideally there would be a message. • This code uses the GoTo construct to jump to the end of the program, and then out, if there is an error • The first GoTo uses a user-defined label as a target. The second one uses a system label GoTo GoTo Considered Harmful • In the “olden days” of programming, there were no structures like If-Then-Else or Do-While • If you didn’t want your program to just execute in a straight line, you used an If and a GoTo: If <condition> Then GoTo <label> • You can write a program that does what you need it to do with just the If and GoTo and some labels, but unless you are a very good and careful programmer, it can be very hard to understand • Starting in the late 1960’s, a big and largely successful effort was made to replace the need for GoTo with structured statements like If-ThenElse, For-Next, and Do-While • The one place where you still often need to jump out of the orderly code execution is when an error occurs • Hence, although GoTo is usually avoided, in handling run-time errors it may often be the best way to go 2 11/9/2011 Other Approaches: Exit Other Approaches: Defaults • Before looking at the GoTo <label> approach, we consider some alternatives • In our first example, we tested the return value of the GetOpenFilename function; if it returns False, we exit the subroutine. This exit is similar to a GoTo in that we are just jumping out of the middle of a subroutine to wherever it was called from. We could also give a message to the user before exiting, using a message box • One common cause of errors is where the user doesn’t enter a required value in a field such as a text box, or enters an unacceptable value • You can set a text box to a default value in the UserForm_Initialize procedure to help prevent such errors. Many shopping sites initialize the quantity box to 0 or 1 for this reason (and also because 1 is the most common value in most cases) Other Approaches: Limit Choices Other Approaches: Scrutiny • Instead of having the users type something in a text box, you can have them choose a value from a drop-down menu • In this way you limit the choices to correct ones • You often see this approach on shopping sites where you choose your state or the expiration year of your credit card from a drop-down list On Error GoTo • Information for this section was found at http://www.cpearson.com/excel/errorhandling.htm • There are three forms of On Error GoTo, which we will consider next • Another way to avoid run-time errors caused by unexpected inputs, such as non-numbers where a number was expected, is to write code that will scrutinize the input string carefully before trying to convert it into a number, and ask the user to re-enter a faulty value • This requires sophisticated use of the string manipulation functions On Error GoTo 0 • The simplest form of the On Error GoTo is On Error GoTo 0 • This makes VBA display a standard run time error message box; you can also enter code in debug mode • Basically this is the same behavior you get if you have no error handler at all, so you should try to do better if you have the time 3 11/9/2011 On Error Resume Next On Error GoTo <label> • The next option is to use On Error Resume Next • With this statement, you are telling VBA that if a run-time error occurs, it should just ignore it and execute the next line • The problem here is that there may be unintended consequences as a result of the error (like a missing value that never got set), so just continuing as if nothing happened could be a bad idea • The third option is to have VBA jump to a label where you try to correct the error, or at least exit gracefully • For example, our file reading program goes to a place at the end of the subroutine where any open file is closed before exiting • A typical structure is to have the ErrorHandler label at the end of the code, with an Exit Sub just before it to exit normally if there were no errors Example Structure Resume Sub Example () On Error GoTo ErrorHandler <normal code> Exit Sub ErrorHandler: Msgbox(“A fatal error has occurred”) End Sub • Instead of exiting your subprocedure after going to the error label, you might want to resume executing it. The Resume statement allows you to do this • There are three forms of resume: – Resume – Resume Next – Resume <label> Plain Resume Error Numbers • This tells VBA to go back to the line that caused the error and try again • Obviously, this means you have to do something to fix the error before going back! • Part of the problem here is that your program needs to know what kind of error occurred • When there is an error, VBA sets a property called Err.Number to communicate the type of error • VBA provides an error description to go with each error number • These can be extremely valuable when you are debugging your code • The next page shows a sample code structure, based on an example found at http://www.ozgrid.com/VBA/ExcelVBAErrors.htm 4 11/9/2011 Showing the Error Description Sub Example() On Error GoTo ErrorHandler <normal code> Exit Sub ‘avoid error handler if no error ErrorHandler: MsgBox Err.Number & “ “ & Err.Description End Sub Resume Next • With Resume Next, the program starts executing at the next line after where the error occurred • As with the previous case, your program should do something to diagnose and fix the error before resuming Resume <label> An Illustration of Checking • Here the program resumes execution at the line with the specified label • This allows you to skip a chunk of code where things might be problematic • Of course you should compensate for the skipped code, if needed, by setting variable values or otherwise patching things up after the error • The application RealEstateErrorHandler shows how to use string functions to scrutinize user input for errors. • It also illustrates checking that an option has been chosen in a list box or a set of option buttons • We’ll look at how each of these is done Checking Listboxes Checking Option Buttons '*** get the agent and neighborhood from listboxes '*** exit if either is null If (lstAgents.ListIndex = -1) Or _ (lstNeighborhoods.ListIndex = -1) Then MsgBox ("You must select an agent and a neighborhood") Exit Sub End If Function CheckCommission() As Boolean '*** check that an option has been chosen If optSolo.Value = True Or optShared.Value = True Then CheckCommission = True Else 'error, no commission chosen CheckCommission = False End If End Function 5 11/9/2011 Checking the Price: Declarations Const DIGITS As String = "0123456789" Dim testStr As String Dim testStrLength As Integer Dim BadCharFound As Boolean Dim j As Integer 'loop control Dim jChar As String ‘jth character in the input string Checking the Price: Main Loop j = 1 'index of first character in testStr BadCharFound = False 'stop as soon as a bad character is found Do While (j <= testStrLength) And (Not BadCharFound) jChar = Mid(testStr, j, 1) If InStr(DIGITS, jChar) = 0 Then BadCharFound = True End If j=j+1 Loop Checking the Price: Initial Steps testStr = txtPrice.Text PriceOK = True 'get ready to check each character in the test string 'each character should be a digit testStr = Trim(inputStr) testStrLength = Len(testStr) 'Take care of the empty input string case If testStrLength = 0 Then PriceOK = False Exit Function End If About Checking • Checking is much more work than just jumping to a label if there is an error • But it is worth it to give the user detailed messages and allow for a graceful recovery if an error happens If BadCharFound Then PriceOK = False End If Sources • Error Handling in VBA, on the Pearson Software Consulting Services site, www.cpearson.com/excel/errorhandling.htm • Appendix C in Walkenbach has a list of all the VBA error codes and their descriptions 6