In Debugging – Part 1, you learned the basics of the debugging process. You learned how to avoid bugs, as well as how to utilize the Immediate window in the debugging process. This article takes things to the next level by showing you how you can use the debugger to interact with your code as it is executing.
Invoking the Debugger
You can invoke the Access debugger in several ways:
Place a breakpoint in your code.
Place a watch in your code.
Press Ctrl+Break while the code is running.
Insert a Stop statement in your code.
A breakpoint is an unconditional point at which you want to suspend code execution. It is temporary because it is in effect only while the database is open. In other words, Access does not save breakpoints with the database.
A watch is a condition under which you want to suspend code execution. You might want to suspend code execution when a counter variable reaches a specific value, for example. A watch also is temporary; Access removes it after you close the database.
A Stop statement is permanent. In fact, if you forget to remove Stop statements from your code, your application stops execution while the user is running it.
Using Breakpoints to Troubleshoot
As mentioned, a breakpoint is a point at which Access will unconditionally halt the execution of code. You can set multiple breakpoints in your code. You can add and remove breakpoints as your code executes.
A breakpoint enables you to halt your code execution at a suspicious area of code. This way, you can examine everything that is going on at that point in your code execution. By strategically placing breakpoints in your code, you quickly can execute sections of code that you already debugged, stopping only at problem areas.
To set a breakpoint, follow these steps:
Place your cursor on the line of code where you want to invoke the debugger.
You can insert a breakpoint in one of four ways: Press your F9 function key.
Click in the gray margin area to the left of the line of the code that will contain the breakpoint.
Click the Toggle Breakpoint button on the Debug toolbar.
Choose Debug, Toggle Breakpoint.
The line of code containing the breakpoint appears in a different color, and a dot appears, indicating the breakpoint.
Run the form, report, or module containing the breakpoint. VBA suspends execution just before executing the line of code where you placed the breakpoint. The statement that is about to execute appears in a contrasting color (the default is yellow).
Now that you have suspended your code, you can step through it one line at a time, change the value of variables, and view your call stack, among other things.
Keep in mind that a breakpoint is actually a toggle. If you want to remove a breakpoint, click in the gray margin area, press F9, or click Toggle Breakpoint on the Debug toolbar. Access removes breakpoints when you close the database, when you open another database, or when you exit Access.
The easiest way to get to know the debugger is to actually use it. The following example gives you hands-on experience in setting and stopping code execution at a breakpoint. The example is developed further later in the article.
Start by creating a form called frmDebug that contains a command button called cmdDebug. Give the button the caption Start Debug Process. Place the following code in the Click event of the command button:
Sub cmdDebug_Click ()
Create a module called basFuncs. Enter three functions into the module:
Sub Func1 ()
Dim intTemp As Integer
intTemp = 10
Debug.Print “We Are Now In Func1()”
Sub Func2 ()
Dim strName As String
strName = “Bill Gates”
Debug.Print “We Are Now In Func2()”
Sub Func3 ()
Debug.Print “We Are Now In Func3()”
MsgBox “Hi There From The Func3() Sub Procedure”
Now you should debug. Start by placing a breakpoint within the Click event of cmdDebug on the line called Call Func1. Here are the steps:
Click anywhere on the line of code that says Call Func1.
Click in the gray margin area, press the F9 function key, click the Toggle Breakpoint button on the Debug toolbar, or choose Debug, Toggle Breakpoint. The line with the breakpoint turns a different color (red by default).
Go into Form view and click the Start Debug Process button. Access suspends execution just before executing the line where you placed the breakpoint. VBA displays the line that reads Call Func1 in a different color (by default, yellow), indicating that it is about to execute that line (see Figure 1).
Stepping Through Code
Access 2010 gives you three main options for stepping through your code. Each one is slightly different. The Step Into option enables you to step through each line of code within a subroutine or function, whereas the Step Over option executes a procedure without stepping through each line of code within it. The Step Out option runs all code in nested procedures and then returns you to the procedure that called the line of code you are on. Knowing the right option to use to solve a particular problem is an acquired skill that comes with continued development experience.
Using Step Into
When you reach a breakpoint, you can continue executing your code one line at a time or continue execution until you reach another breakpoint. To step through your code one line at a time, click Step Into on the Debug toolbar, press F8, or choose Debug, Step Into.
The following example illustrates the process of stepping through your code, printing the values of variables to the Immediate window, and modifying the values of variables using the Immediate window.
You can continue the debug process from the breakpoint you set in the previous example. Step two times (press F8). You should find yourself within Func1, about to execute the line of code intTemp = 10 (see Figure 2). Notice that VBA did not stop on the line Dim intTemp As Integer. The debugger does not stop on variable declarations.
The code is about to print the Debug statements to the Immediate window. Take a look by opening the Immediate window. None of your code has printed anything to the Immediate window yet. Press F8 (step) three more times until you have executed the line Debug.Print intTemp. Your screen should look like Figure 3. Notice the results of the Debug.Print statements.
Now that you have seen how you can display variables and the results of expressions to the Immediate window, take a look at how you can use the Immediate window to modify values of variables and controls. Start by changing the value of intTemp. Click the Immediate window and type intTemp = 50. When you press Enter, you actually modify the value of intTemp. Type ?intTemp, and you’ll see that Access echoes back the value of 50. You also can see the value of intTemp in the Locals window. Notice in Figure 4 that the intTemp variable appears along with its value and type.
Executing Until You Reach the Next Breakpoint
Suppose that you have reached a breakpoint, but you realize that your problem is farther down in the code execution. In fact, the problem is actually in a different function. You might not want to continue to move one step at a time down to the offending function. Use the Procedure drop-down menu to locate the questionable function and then set a breakpoint on the line where you want to continue stepping. You now are ready to continue code execution until Access reaches this line. To do this, click Continue on the Debug toolbar, press F5, or choose Run, Continue. Your code continues to execute, stopping at the next breakpoint. To see how this works, continue the Debug process with the next example.
You also can opt to resume code execution to the point at which your cursor is located. To do this, select Run to Cursor from the Debug menu, or press Ctrl+F8.
Suppose that you realize your problem might be in Func3. You do not want to continue to move one step at a time down to Func3. No problem. Use the Procedure drop-down menu to view Func3, as shown in Figure 5. Set a breakpoint on the line that reads Debug.Print "We Are Now In Func3()". You are ready to continue code execution until Access reaches this line. Click Continue on the Debug toolbar, press F5, or choose Run, Continue. Your code continues to execute, stopping on the breakpoint you just set. Press F5 again. The code finishes executing. Return to the Form View window.
Using Step Over
Sometimes you already have a subroutine fully tested and debugged. You want to continue stepping through the routine that you are in, but you don’t want to watch the execution of subroutines. In this case, you use Step Over. To step over a subroutine or function, click Step Over on the Debug toolbar, press Shift+F8, or choose Debug, Step Over. The code within the subroutine or function you are stepping over executes, but you do not step through it. To experiment with the Step Over feature, follow the next example.
Click the open form and click the Start Debug Process button one more time. Because you did not remove the existing breakpoints, Access places you on the line of code that reads Call Func1. Select Clear All Breakpoints from the Debug menu or use the Ctrl+Shift+F9 keystroke combination to remove all breakpoints. Step (press F8) five times until you are about to execute the line Call Func2. Suppose that you have tested Func2 and Func3 and know that they are not the cause of the problems in your code. With Func2 highlighted as the next line Access will execute, click Step Over on the toolbar. Notice that Access executes Func2 and Func3, but that you now are ready to continue stepping in Func1. In this case, Access places you on the End Sub line immediately following the call to Func2.
Using Step Out
You use the Step Out feature to step out of the procedure you are in and to return to the procedure that called the line of code you are on. You use this feature when you have accidentally stepped into a procedure that you realize you have fully tested. You want to execute all the code called by the procedure you are in and then step out to the calling procedure so that you can continue with the debugging process. To test how this works, follow this example.
Place a breakpoint on the call to Func2.
Click the Reset button on the toolbar to halt code execution.
Activate the frmDebug form and click the Start Debug Process command button.
Step once to place yourself in the first line of Func2.
Suppose that you realize you just stepped one step too far. You really intended to step over Func2 and all the procedures it calls. No problem! Click the Step Out button to step out of Func2 and return to the line following the line of code that called Func2. In this case, you should find yourself on the End Sub statement of Func1.
Setting the Next Statement to Execute
After you have stepped through your code, watched the logical flow, and modified some variables, you might want to re-execute the code beginning at a prior statement. The easiest way to do this is to click and drag the yellow arrow in the margin to the statement on which you want to continue execution. If you prefer, you can click anywhere in the line of code where you want to commence execution and then choose Debug, Set Next Statement. Regardless of the method you chose, notice that the contrasting color (usually yellow, indicating the next line of code that Access will execute, is now placed over that statement. You then can step through the code by pressing F8, or you can continue normal code execution by pressing F5. Access enables you to set the next line it will execute within a procedure only. You can use this feature to re-execute lines of code or to skip over a problematic line of code.
The following example walks you through the process of changing the value of a variable and then re-executing code after you have changed the value.
The preceding example left you at the last line of code (the End Sub statement) within Func1. Now you want to change the value of intTemp and re-execute everything:
Go to the Immediate window and type intTemp = 100.
You need to set the next statement to print on the line that reads Debug.Print “We Are Now in Func1()”. To do this, click and drag the yellow arrow from the End Sub statement to the Debug.Print “We Are Now In Func1()” line. Notice the contrasting color (yellow), indicating that that is the next statement of code Access will execute.
Press F8 (step) two times. The code now executes with intTemp set to 100. Observe the Immediate window again. Notice how the results have changed.
Using the Call Stack Window
You have learned how to set breakpoints, step through and over code, use the Immediate window, set the next line to be executed, and continue to run until you reach the next breakpoint. When you reach a breakpoint, it often is important to see which functions the code called to bring you to this point. In this case, the Calls feature can help.
To bring up the Call Stack window, click the Call Stack button on the toolbar or choose View, Call Stack. The window in Figure 6 appears. If you want to see the line of code that called a particular function or subroutine, double-click that particular function or click the function and then click Show. Although Access does not move your execution point to the calling function or subroutine, you are able to view the code within the procedure. If you want to continue your code execution, press F8. You move back to the procedure through which you were stepping, and the next line of code executes. If you press F5, your code executes until it reaches another breakpoint or watch. If you want to return to where you were without executing additional lines of code, choose Debug, Show Next Statement.
To test this process, perform the next example:
Click the Reset button to stop your code execution if you are still in Break mode.
Remove the breakpoint on the call to Func2.
Move to the procedure called Func3 in basFuncs. Set a breakpoint on the line Debug.Print “We Are Now in Func3()”. Run the frmDebug form and click the command button. Access places you in Func3 on the line where you set the breakpoint.
Bring up the Call Stack window by clicking the Call Stack button on the toolbar. If you want to see the line of code that called Func2 from Func1, double-click Func1. Although Access does not move your execution point to Func1, you are able to view the code within the procedure.
To return to the next line of code to execute, choose Debug, Show Next Statement.
Press F5, and the rest of your code executes.
Working with the Locals Window
The Locals window enables you to see all the variables on the current stack frame and to view and modify their values. To access the Locals pane, click Locals Window on the toolbar, or select Locals Window from the View menu. Three columns appear: Expression, Value, and Type. The Expression column shows you the variables, user-defined types, arrays, and other objects visible within the current procedure. The Value column displays the current value of a variable or expression. The Type column tells you what type of data a variable contains. The Locals window displays variables that contain hierarchical information, arrays, for example, with an Expand/Collapse button.
The information contained within the Locals window is dynamic. Access automatically updates it as it executes your code and as you move from routine to routine. Figure 7 illustrates how you can use the Locals window to view the variables available with the Func2 subroutine. To try this example yourself, remove all existing breakpoints. Place a breakpoint in Func2 on the line of code that reads Debug.Print strName. Click Reset if you are still executing code and click the Start Debug Process command button to execute code until the breakpoint. Click the Locals Window button on the Debug toolbar. Click the plus sign to view the contents of the public variable gintCounter.
You can change the value of a variable in the Locals window, but you cannot change its name or type.
Working with Watch Expressions
Sometimes it is not enough to use the Immediate window to test the value of an expression or variable. You might want to keep a constant eye on the expression’s value. To do so, you can set a watch expression. After you add a watch expression, it appears in the Watch window. As you’ll see, you can create several types of watches.
Using Auto Data Tips
The quickest and easiest way to view the value contained within a variable is to use Auto Data Tips, which is an option for working with modules. This feature is available only when your code is in Break mode. While in Break mode, simply move your mouse pointer over the variable or expression whose value you want to check. A tip appears with the current value. To set the Auto Data Tips option from the VBE, choose Tools, Options, click the Editor tab, and check the option for Auto Data Tips, which is under the Code Settings options.
Using a Quick Watch
A quick watch is the most basic type of watch. To add a quick watch, highlight the name of the variable or expression you want to watch and click the Quick Watch button on the toolbar. The Quick Watch dialog box, shown in Figure 8, appears. You can click Add to add the expression as a permanent watch or choose Cancel to view the current value without adding it as a watch. If you click Add, the Watches window appears, like the one in Figure 9. The next section discusses this window in more detail.
Adding a Watch Expression
As you saw, you can add a watch expression using a quick watch. Adding a watch this way does not give you full control over the nature of the watch, however. If you need more control over the watch, you must choose Debug, Add Watch. The Add Watch dialog box appears, as shown in Figure 10.
If you add a quick watch or add a watch by choosing Debug, Add Watch, you easily can customize the specifics of the watch by clicking with the right mouse button over the watch in the Watches window. Then select Edit Watch.
A quick way to add a watch to the Watches window is to click and drag a variable or expression from a Code module into the Watches window. Access adds the watch with default settings.
In the Expression text box, enter a variable, property, function call, or any other valid expression. It is important to select the procedure and module in which you want to watch the expression. Next, indicate whether you want to simply watch the value of the expression in the Immediate window, break when the expression becomes True, or break whenever the value of the expression changes. The sections that follow cover the two latter options.
The next example walks you through the process of adding a watch and viewing the watch variable as you step through your code. It illustrates how a variable goes in and out of scope, and changes value, during code execution.
To begin, stop code execution if your code is running and remove any breakpoints you have set.
Click within the strName variable in Func2.
Right-click and choose Add Watch.
Click OK to accept the Func2 procedure as the context for the variable and basFuncs as the module for the variable.
Set a breakpoint on the line strName = "Bill Gates".
Run the frmDebug form and click the command button. View the Watches window and notice that strName has the value of a zero-length string.
Step one time and notice that strName is equal to Bill Gates.
Step three more times. Notice that, although you are in the Func3 routine, strName still has the value Bill Gates. The reason is that the variable is still in memory in the context of basFuncs.Func2.
Step four more times until you are back on the End Sub statement of Func2. The strName variable is still in context.
Step one more time. The strName variable is finally out of context because you have completed the execution of Func2.
Editing a Watch Expression
After you add a watch, you might want to edit the nature of the watch or remove it entirely. You use the Edit Watch dialog box to edit or delete a watch expression. Follow these steps:
Activate the Watches window.
Select the expression you want to edit.
Choose Debug, Edit Watch, or right-click and choose Edit Watch. The Edit Watch dialog box.
Make changes to the watch or click Delete to remove it.
Breaking When an Expression Is True
A powerful aspect of a watch expression is that you can break whenever an expression becomes True. You can break whenever a Public variable reaches a specific value, for example. You might want to do this when a Public or Private variable somehow is being changed, and you want to find out where. Consider the following code, located in the basFuncs module of CHAP16EX.ACCDB:
gintCounter = 50
gintCounter = gintCounter + 10
Dim intCounter As Integer
For intCounter = 1 To 10
gintCounter = gintCounter + intCounter
You might find that gintCounter somehow is reaching a number greater than 100, and you are not sure how. To solve the problem, add the watch shown in Figure 11. Notice that the expression you are testing for is gintCounter > 100. You have set the breakpoint to break the code whenever the expression becomes True. To test the code, type ChangeGlobal1 in the Immediate window and press Enter. The code should break in the ChangeGlobal3 routine, indicating that this routine is the culprit.
Breaking When an Expression Changes
Instead of breaking when an expression becomes True, you might want to break whenever the value of the expression changes. This is a great way to identify the place where something is mysteriously modifying the value of a variable. Like Break When Value Is True, the Break When Value Changes option is great for tracking down problems with Public and Private variables. Notice the watch being set in Figure 12. It is in the context of all procedures within all modules. It is set to break whenever the value of gintCounter changes. If you execute the ChangeGlobal1 routine, you’ll find that the code halts execution within ChangeGlobal1 immediately after the code sets the value of gintCounter to 50. If you press F5 to continue execution, the code halts within ChangeGlobal2 immediately after it increments the value of gintCounter by 10. In other words, every time the code modifies the value of gintCounter, the code execution breaks.
Continuing Execution After a Runtime Error
As you are testing, you often discover runtime errors that are quite easy to fix. When a runtime error occurs, a dialog box similar to the one shown in Figure 13 appears.
If you click Debug, Access places you in the Code window on the line that generated the error. After rectifying the problem, click the Continue button on the toolbar, or choose Run, Continue.
Figure 14 shows a divide-by-zero error, for example, after the user clicked Debug from the Runtime Error dialog box. The Locals window in the figure shows that the programmer set the value of int2 to 20. Code execution now can continue without error.
Often, after an error occurs, VBA displays a message giving you the option of resetting your code. If you opt to reset your code, all variables (including Publics and Statics) lose their values. You also can click Reset on the toolbar. You must decide whether it is better to proceed with your variables already set or to reset the variables and then proceed.
The General tab of the Options dialog box allows you to configure how VBA error handling and the debugger interact.
Looking at Gotchas with the Immediate Window
Although the Access debugger is excellent, the debugging process itself is wrought with an array of potential problems, as follows:
The debugging process can interrupt code execution, especially when forms are involved. When this occurs, the best bet is to place Debug.Print statements in your code and examine what happens after the code executes.
Along the lines of the preceding problem, it is difficult to debug code where you have coded the GotFocus and LostFocus events. Moving to the VBE triggers the LostFocus event of the control. Returning to the form causes Access to trigger the GotFocus event of the control. Once again, a great solution is Debug.Print. You also might consider writing information to an error log for perusal after the code executes.
Code that uses Screen.ActiveForm and Screen.ActiveControl wreaks havoc on the debugging process. When the VBE is active, there is no active form and no active control. Avoiding these lines in your code wherever possible alleviates this problem.
Finally, be aware that resetting code can cause problems. If you are modifying environmental settings, you are left with whatever environmental settings your application code changed. If you continue execution after the error without resetting, all sorts of other problems can occur. It is a good idea to code a special utility routine that resets your environment.
You use assertions to ensure that, if the user encounters a certain state, your code invokes the debugger. The following code, found in basExamples, is an example:
Dim intAge As Integer
intAge = InputBox(“Please Enter Your Age”)
Debug.Assert (intAge >= 0)
MsgBox “You are “ & intAge
This example sets the value of a variable called intAge equal to the value entered into an Input Box. The Debug.Assert statement “asserts” that the value entered is greater than or equal to zero. If it is, code execution proceeds as expected. If the assertion is incorrect, the code invokes the debugger.
It is a good idea to include a comment as to why an assertion might fail. By doing this, you will facilitate the process of responding to the situation when it occurs. Also, it is important to realize that, if you deploy your application with Debug.Assert statements intact, you will receive a technical support call when an assertion fails, and your code places the user in the debugger without warning!
The following tips will make your life much easier when debugging:
Before starting to debug, be clear about what the problem is. Make sure that you get all the necessary information from the user as to what he did to generate the problem. Without this vital information, you can spend countless hours trying to reproduce the problem rather than solve it.
Make changes one line of code at a time. I have seen many hot-shot developers attempt to change multiple lines of code simultaneously. Instead of correcting the problem they initially set out to solve, they generate a multitude of additional problems.
Talk out the problem with other developers. Sometimes the process of simply verbalizing the problem can be enough to help you to figure it out. If verbalizing the problem doesn’t provide you with the answer, the person that you are verbalizing to might know the answer.
When all else fails, take a break. Many times I have stayed up into the wee hours of the night, attempting to solve a problem. After finally giving up, I surrender and go to bed. It’s amazing how many times I solve the “unsolvable” problem from the night before while in the shower the next morning!
If programming were a perfect science, there would be no reason to use a debugger. Given the reality of the challenges of programming, a thorough understanding of the use of the debugger is imperative. Fortunately, the Access 2010 VBE provides an excellent tool to assist in the debugging process.
In this article you learned how to step through your code, using watches and breakpoints, as well as how to view the call stack. All these techniques help make the process of testing and debugging your application a pleasant experience.
This article was excerpted from Alison Balter's Mastering Microsoft Office Access 2007 Development
published by SAMS.