Now let's set up the Visual Basic .NET IDE to exploit the debugger. The first thing you might want to do is activate the debugger toolbar. You do this by selecting the View, Toolbars, Debug menu sequence. This is shown in Figure 19.4.
Figure 19.4. Activating the Debug toolbar.
When you do this, the Debug toolbar appears just above the Forms window. The new toolbar is shown in Figure 19.5.
Figure 19.5. The Debug toolbar.
The Debug toolbar provides the following list of options:
Although you might not use all the debugger's features on a regular basis, it's nice to have all of these features in those cases when you do need them.
Setting a Breakpoint
There are two ways to set a breakpoint. The easiest is to move the cursor to the line on which you want to set the breakpoint and press the F9 key. If you press the F9 key a second time while the cursor is on the breakpoint line, the breakpoint is removed. Therefore, the F9 key is a toggle that may be used to turn a breakpoint on or off.
There is a second way to set a breakpoint. Move the cursor into the gray margin to the left of the code window even with the line on which you want to set the breakpoint, and click the mouse. The familiar burnt-red breakpoint line should appear in the Code window. Click on the burnt-red dot in the margin and the breakpoint is removed.
As an experiment, set a breakpoint on the code line
ValidErrors += 1
as shown in Listing 19.1. Now compile and run the program. Your program should look similar to Figure 19.6.
Figure 19.6. The state of the program at a breakpoint.
Program execution is now suspended and we're free to inspect the state of the program.
The Locals Window
Suppose you want to examine all the local variables in the program. Recall that local scope applies to all variables that are currently visible in the procedure being executed.
To view the local variables, you need to open the Locals window. To do this, select the Debug, Windows , Locals menu sequence or press the Ctrl+Alt+V keys, and then press the L key. This will open the Locals window near the bottom of the screen, as shown in Figure 19.7.
Figure 19.7. The Locals window.
As you can see, variable i has the value 3 , temp equals our UNUSED_ERROR_NUMBER symbolic constant, and ValidErrors is . The other lines in the window are objects that are also in scope. You might want to click on the small plus sign (+) in the box in front of the variables to expand these entries so that you can see what's being hidden from sight.
The Autos Window
Now let's open the Autos window. To do this, use the Debug, Windows, Autos menu sequence or use the Ctrl+Alt+V keys, and then press the A key. The Autos window will overlay the Locals window. This can be seen in Figure 19.8.
Figure 19.8. The Autos window.
The Autos window shows information that appears to be almost the same as the Locals window, but it is slightly different. The Autos window shows you all the values for the variables that appear in the current (breakpoint) line and the line immediately preceding it. If you single-step to the next line, the Autos window is updated.
Other important things to notice in Figure 19.8 are the two tabs at the bottom-left labeled Autos and Locals. These two tabs enable you to alternate between the Autos and Locals windows by simply clicking the appropriate tab. Note that as we examine other windows, new tabs might appear in this location. This provides a convenient means for activating several debug windows at the same time without cluttering the screen.
The Watch Window
During the debugging process, it is quite often the value of a particular variable that indicates there's a bug. Although the Autos window is helpful for viewing variables on the current and preceding line, those lines might not hold the variable you're tracking. The Watch window provides a better way to observe a variable as the program executes.
The Watch window is selected by using the Debug, Watch, Watch ? menu sequence (where the question mark could be one of four Watch windows), as shown in Figure 19.9.
Figure 19.9. The Watch window.
As you can see, you can set multiple Watch windows. If you select the first Watch window, it's added to the list of available Debug windows as can be seen by the new Watch tab at the bottom left of Figure 19.10.
Figure 19.10. The Watch tab has been added.
In Figure 19.10, we moved the cursor to the first line of the Watch window and typed in the name of a variable; i in this example. The Watch window then shows the current value of i at this point in the program. You can type in additional variables you want to observe while the program executes.
Why does the debugger provide for up to four different Watch windows? In more complex programs, you might want to track a different set of variables at different points in the program. By having different Watch windows, you can have a less-cluttered window in which to watch the variables you're interested in at different points in the program. You can switch between the different Watch windows by simply clicking on the appropriate tab at the bottom of the window display.
A new feature has been added to the debugger, and it's one of those "I-wish-I-had-this-five- years -ago" features you've always wished for but never had. This new feature enables you to set a breakpoint and then activate the breakpoint after that line has been hit a specified number of times. In Figure 19.11, I selected the New Breakpoint option from the Debug menu (or use Ctrl+B) and then clicked the Hit Count button to present the screen you see in Figure 19.11. If you select one of the options from the list box, a text box appears to the right of the list box. You can see that I typed in 50 after selecting the Break When the Hit Count Is Equal To option.
Figure 19.11. Setting the hit counter.
By selecting this option, I'm telling the debugger to run at full speed until the breakpoint line has been executed (that is, hit) 50 times. Only then is the breakpoint executed and the program paused . Although there are other ways to accomplish this type of breakpoint, this approach is very convenient to use. It sure beats banging on the F5 key 49 times to get to the desired iteration in a loop!
The Command Window
The Command window provides a means for you to interact with your code while debugging a program. You activate the Command window using the Debug, Windows, Immediate menu sequence or by pressing Ctrl+G. You'll see the Command window appear near the lower-right side of the screen.
Once the Command window is activated, you can type in expressions and have Visual Basic .NET use those expressions during program execution. For example, in Figure 19.12, I've typed
ValidErrors = 100
Figure 19.12. The Command window.
into the Command window. This expression sets the value of ValidErrors to 100 . In other words, the Command window is handy for entering values into the code that you might want to test.
Also note in Figure 19.12 that you can still access the Output window via the tab that appears below the Command window. These tabs enable you to toggle between the Command and Output windows as needed.
Being able to set the values with the Command windows makes it easy to check certain values that you think might cause problems in the program. A common area of program bugs is in what are called boundary values. A boundary value is the first or last value in a set of values that a variable is expected to acquire during program execution. For example, if you have a For loop that's expected to run from 0 to 100, the boundary values for the loop are and 100 . However, perhaps the value doesn't really cause a certain program sequence or value to appear as expected. Likewise, the value 100 might not produce the expected results, either. The Command window makes it easy to type in the boundary values and assess their impact on the program.
Even though boundary values aren't always the culprit for a program error, they're often a good place to start the debugging process. This is especially true in loops that involve array processing.
As you can tell, there are a number of other windows that Visual Basic .NET makes available to you. For example, the Call Stack window shows you the name of each function your program calls and the language it's written in. Likewise, if you want to see what your program would look like if it were written in Assembly language, you could activate the Disassembly and Registers windows. (If nothing else, doing so confirms that using Visual Basic .NET is a lot easier to use than Assembly code!)
Although these other windows might be interesting and helpful in certain situations, I won't discuss these here. You can always try experimenting with them, using the Online Help to guide your experimentation. At this point, however, you've been exposed to enough debugging features to track down the vast majority of program bugs.
There is one more thing you might find useful. As I said at the beginning of this chapter, one of your first tasks in debugging a program is to isolate the bug. This often mean setting a lot of breakpoints at different places in the program. You'll eventually narrow your search to one specific section of code. However, in the process of getting there, each time you rerun the program, you have to wade through a dozen or more old (that is, no longer useful) breakpoints to get to the point of interest.
Even though you could toggle each of these old breakpoints off, it's much easier to clear all the breakpoints at once. First, move the cursor to the line where you want to set a breakpoint after you clear all the old breakpoints. Now use the Debug, Clear All Breakpoints menu sequence, or use the Ctrl+Shift+F9 keystrokes to remove all the breakpoints. Finally, press the F9 key to toggle the new breakpoint. The program will now run at normal speed to the line where you set the new breakpoint.
For the remainder of this chapter, I'll show you how to set traps for bugs before they even happen.