Debugging in Visual Basic .NET
Microsoft® Visual Studio® .NET
Microsoft Visual Basic® .NET
Summary: Learn creative ways to debug applications such as using debug windows, making changes while debugging and using the command and output windows. Debugging in VS.NET changes the way developers work with and debug applications. Part of this change results from the migration from the VB 6 environment to VS.NET environment.
Creating the Life Insurance Calculator
The Debug Menu
A Robust Command Window
Command Window: Immediate Mode
The Output Window
The Breaking Point
Correcting the Life Insurance Calculator
Now, Where Was I?
Using the Debug and Trace Classes
- Create and debug a simple Microsoft® Windows® application in Microsoft Visual Basic® .NET using the debug and output windows.
- Demonstrate the use of the different Command windows.
- Add breakpoints and bookmarks to our test application and show how they can be valuable tools.
- Exhibit the Debug and Trace classes and some useful methods.
We all have them, those tricks and workarounds to aid us with debugging a program during development. While there are many useful tools available to us within the VB6 environment, there always seems to be something else desired. VB.NET has responded to these lingering needs and added many brilliant and innovative debugging features.
The simple but much used Command Window has been enhanced beyond its modest beginnings, while breakpoints and bookmarks leverage the new standards set by an ever changing internet setting. The Debug and Trace classes expand an already rich development environment. Not only will VB.NET programmers find their debugging easier, development of applications will fundamentally change in response to these tools.
In order to demonstrate some of the debugging features, we must first have something to debug. We are going to create a simple VB application that will calculate the cost of life insurance based on a salary rounded to the nearest thousand. Let's suppose this is a "what-if" scenario type of window that is just a small part of a larger application. We'll use this simple VB form to input all the variables and then click a button to calculate the results. Then we'll run the application using the debug and output windows so that we can see how they assist in making corrections or additions to the program.
Begin by opening Microsoft Visual Studio® .NET and selecting the New Project button from the Start Page, or by selecting File, New, and then Project from the main menu. Select "Visual Basic Projects" for the project type and "Windows Application" for the template. Let's name our life insurance calculator "LifeInsCalc". Select any suitable folder for our project location, and click the OK button.
Now we should be in the design mode for Form1. Let's change the form's Text property to "Life Insurance Cost Calculator". Now we're ready to add some controls.
- Drag five Label controls from the Toolbox and drop them onto the left side of the form.
- Set the Text properties on the five Labels as follows:
Label1 Text = "Employee's Annual Salary:"
Label2 Text = "Rounded Salary:"
Label3 Text = "Ins. Cost per $1000:"
Label4 Text = "Total Cost of Insurance:"
Label5 Text = "Bi-weekly Cost of Insurance:"
- Next, drag four TextBox controls onto the form and place one to the right of each of the "Employee's Annual Salary:", "Total Cost . . . " and "Bi-weekly Cost . . . " labels. Set their properties as follows:
TextBox1 Name = "txtSalary" Text = "0.0"
TextBox2 Name = "txtRoundSalary" Text = "" ReadOnly = True
TextBox3 Name = "txtTotalCost" Text = "" ReadOnly = True
TextBox4 Name = "txtBiWeeklyCost" Text = "" ReadOnly = True
- Add a NumericUpDown control, aligning it with the "Cost per $1000:" label. Set the name property to "numUnitCost". Next, set the following properties:
- Value .005
- Drag two Button controls onto the form and arrange them side-by-side below the TextBox controls. Set their properties as follows:
Button1 Name = "btnCalculate" Text = "Calculate"
Button2 Name = "btnClear" Text = "Clear"
Resize the form as desired to achieve a small simple window as follows:
Now that we have the basic form designed, let's add the code behind the buttons. Double-click the btnClear control to add code to reset the values of the controls on the form and return the focus to the txtSalary control.
Private Sub btnClear_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnClear.Click ' Clear the controls on the form txtSalary.ResetText() txtRoundSalary.ResetText() txtTotalCost.ResetText() txtBiWeeklyCost.ResetText() numUnitCost.ResetText() txtSalary.Focus() End Sub
Of course the btnCalculate button will do all the work, so this code will be a bit more complex. This code will take the amount entered into the Salary field and round it up to the nearest thousand, and then multiply the result by the unit cost for the insurance. The product of that calculation will be our Total Cost.
Double-click the btnCalculate control and paste the following code:
Private Sub btnCalculate_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnCalculate.Click ' The local calculation variables Dim EmpSalary As Double Dim RoundSalary As Single Dim UnitCost As Double Dim TotalCost As Single Dim BiWeeklyCost As Single ' Set local variables based on inputs EmpSalary = CDbl(txtSalary.Text) UnitCost = CDbl(numUnitCost.Value) ' Round the Salary up to the nearest thousand RoundSalary = Int(EmpSalary) ' Calculate the Total Insurance Cost TotalCost = UnitCost * RoundSalary BiWeeklyCost = TotalCost / 26 ' Update the Rounded Salary and Insurance Cost Textboxs txtRoundSalary.Text = RoundSalary txtTotalCost.Text = TotalCost txtBiWeeklyCost.Text = BiWeeklyCost End Sub
Now our application is ready to test. We can simply click the Run button on the toolbar or hit the F5 key to begin the application. Try out the calculator using an uneven number for the salary, like 21,189.99. This should give the program a good test.
Well, even if the application works and calculates, it should become apparent there are some problems already. The Rounded Salary field simply isn't correct if it should be rounding to the nearest thousand. So, we begin to debug. Let's look at how that can be accomplished in VB.NET.
The Debug menu is a natural choice to start debugging any program under construction, so we'll review the menu options available there. The first thing you might notice is the existence of options that were under the Run menu in VB 6. The familiar commands like Start, Restart, Break and so on, are now here. A few options are renamed. Start with Full Compile, for instance, is now Start without Debugging. Once we begin to debug, you might also notice that End has become Stop Debugging.
A few options that existed on the VB 6 View menu have also migrated to the VB.NET Debug menu, such as the Immediate Window, the Call Stack, the Watch windows and the Locals Window.
Some options on this menu may not be seen when in design mode. They will appear as they become available during execution and debugging.
The Command window is used to either issue commands or to debug and evaluate expressions in the IDE. The Command window has two different modes, Command and Immediate.
The Command Mode is used for executing commands directly in the Visual Studio .NET environment, bypassing the menu system, or for executing commands that do not appear in any menu. To open the Command window select the View Menu then click Other Windows and then Command Window, or simply press CTRL+ALT+A.
For example, to display the New File dialog box that would normally be accessed through the File menu, you can type the following command into the Command Window:
IntelliSense appears and facilitates completing the selection easier. In case you are thinking, "why not just use my mouse?", we'll take this a step further. Adding arguments to these commands begins to open up the possibilities. Continuing with our example, we can expand on this by adding a switch that will create a new file and bypass the dialog box:
>File.NewFile lifeins /t:"General\XML File"
This creates a new xml file, called lifeins.xml, based on the XML file template. The /t:templatename argument syntax emulates the way the selections would take place in the New File dialog, where you enter the category name followed a backslash (
\) and then the template name:
As we shall see next, the Immediate mode is used more for debugging purposes. In this mode, we can evaluate expressions, execute statements, display variable values and much more.
The Immediate mode of the Command window allows you to enter expressions, execute statements, print variable values, change variable values (in some cases), etc. The window permits these functions during debugging, so we will combine this with the breakpoint discussion below to assist us in making changes to the Life Insurance Calculator application we started.
To open the Immediate mode of the Command Window select the Debug menu and click the Windows and then Immediate, or press CTRL+ALT+I. When the Command window is in Immediate mode, the title bar displays the text Command Window - Immediate.
Making life even easier, the Immediate mode now also supports IntelliSense.
The UP ARROW and DOWN ARROW keys do not move the cursor to previous commands, but rather allow you to scroll through previously issued commands. You can copy all or part of a previous command to the input line by scrolling to it, highlighting all or part of it, and then pressing ENTER.
If you wish to enter commands while in the immediate mode, it is not necessary to open the Command window. Simply preface any command in the Immediate Mode with a greater than sign (>). For example, to switch to Command mode from Immediate mode, you would enter this into the input line:
To switch back to the Immediate Command window you would enter this command:
In order to demonstrate the Immediate Command window, let's run the Life Insurance Calculator and invoke the Break All command in that window. This will pause execution so we can test a few expressions in the Immediate Command window.
Verify that the Immediate Command window is open by selecting CTRL+ALT+I. Now start the program by clicking the Run button on the toolbar or hitting the F5 key. Next, break or pause execution by clicking the Break All button on the toolbar, or selecting the Debug menu then the Break All option. The Immediate Command window should be open below the code window.
Now we'll restart or continue program execution using a command in the window. Since we are using the Immediate mode, remember to preface any commands with greater than sign (>). IntelliSense will help, but most commands mimic the menu system:
In this same way we could also halt execution again:
As you can see, the Command window has opened up a new avenue for controlling the IDE in VB.NET.
Now we'll stop the program and return to development:
We'll test more expressions and use the Immediate mode further after we introduce the Output window and Breakpoints.
This window displays status messages for various features in the IDE. The window consists of multiple panes that can be accessed by the combo box selection just below the title. These separate panes can display build messages, debug messages, or they can even be configured to output from .bat or .com files that would normally show up in a DOS window.
To access the Output window, select Other Windows from the View menu, and choose Output, or select CTRL+ALT+O.
Start the Life Insurance Calculator program again. Note the messages displayed at this point in the Output window. The Build pane should be visible as the application initializes and messages relating to building and starting the program will scroll by. As the calculator form becomes visible, the Debug pane of the Output window will be displayed.
If the Output window is not visible for any reason, simply select CTRL+ALT+O at any time, or locate the tabs that will toggle between it and the Command window.
As you can see, the Debug pane of the Output window displays information regarding the current execution of the program.
Breakpoints are still a great way to stop the execution of your program at predefined points in your code. Applying the breakpoints is an easy task. Simply open a section of code and click on the far left of the design window in the gray area, as in the example below.
Right-click over the breakpoint indicator to display a menu of additional options.
As a side note, it is worth mentioning Outlining. Outlining is a tool for organizing code into a more readable format. Highlight a portion of code, then right-click and select the Outlining submenu. Hide Selection will collapse the marked selection of code and put an indicator (+ . . .) to be used to expand it again as needed. Also the hidden code can be quickly viewed without expanding by simply performing a mouse-over of the collapsed code indicator.
The Breakpoints window is a practical dialog that will display all of your marked breakpoints and certain settings associated with each. To open this window, select the Debug menu, Windows then Breakpoints, or press CTRL+ALT+B. The window contains a toolbar and a list of breakpoints:
OK, we can now place a breakpoint in the btnCalculate Click method just as the RoundSalary variable is being assigned. This way we can use the immediate mode of our command window to test a few expressions and see what will work best to correct the calculating of the next thousand in the Salary field. Double-click the Calculate button to open the Click method and locate this portion of the code:
' Round the Salary up to the nearest thousand RoundSalary = Int(EmpSalary)
Click on the border to the left of the
RoundSalary = Int(EmpSalary) statement to apply the bookmark. Now, make certain the Immediate Command window is open (CTRL+ALT+I), and run the application to test and debug.
Enter the value "31,121.98" in the Salary textbox after the calculator window is displayed. Select the Calculate button and note that the code pane opens with the Immediate window beneath it as the breakpoint is reached.
Try the mouse-over feature and notice that you can still get variable values to display in the code pane during debugging. The RoundSalary variable should have a value of 0.0 because this line has not executed yet. We can evaluate several expressions in the Immediate Command window below to zero-in on the correct "rounding".
The world of insurance is a wondrous one complete with its own math. In this case, no matter what figure is entered, it must be changed to the next increment of 1,000. So if the salary is 20,000.99, it must be rounded to 21,000.00.
Type the variable assignment,
RoundSalary = Int(EmpSalary), in the Immediate Command window and then ENTER. The mouse-over feature in the code pane now reveals the RoundSalary variable has a value of 31121.0, not exactly what we were after. Back in the Immediate Command window, you can also display the assigned value by typing the print function (?RoundSalary), or even just doing a mouse-over of the RoundSalary variable there.
Within the Immediate Command window, we can now begin to test all sorts of scenarios and see what expression is going to give the desired result:
?int((EmpSalary + 1000)/1000) * 1000
The resulting value is 32000. This looks pretty good, but replacing the EmpSalary variable with a variety of possible salaries can expose any trouble spots:
?int((20000 + 1000)/1000) * 1000
This results in a value of 21000, which is not even correct in insurance math. There is a Ceiling method within the Math function class that will return the smallest whole number greater than or equal to the specified number. This suits our purposes when used this way:
RoundSalary = Math.Ceiling(EmpSalary/1000)*1000
Voila! No matter what salary value is entered, the proper rounded amount results. Now that we have set the RoundSalary variable, we can skip over the assignment statement in our code and instead execute whatever statement we choose. Place the mouse over the breakpoint and drag the yellow arrow down to the TotalCost variable assignment below.
This will cause execution to begin here when we start again. Select the Debug menu and then Continue, or click on the Run button on the toolbar. As we resume the calculator application, the resulting rounded salary and total and bi-weekly cost of insurance figures are now correctly being calculated.
Since you will no doubt wish to change the code for the RoundSalary variable assignment, it is important to note a change from VB 6.0. In Visual Basic 6.0, in most cases you could make changes to your code while in break mode and continue debugging without stopping and restarting. In Visual Basic .NET, any code changes in break mode require that the project be rebuilt before the code changes can take effect; edit and continue is no longer supported.
Stop the Life Insurance Calculator and make the change in the btnCalculate Click event as follows:
' Round the Salary up to the nearest thousand RoundSalary = Math.Ceiling(EmpSalary / 1000) * 1000
Now, run the application again to test the changes.
Although our sample code is somewhat simple and easily navigated, it doesn't take long for any application to become complex. Let's look at another tool for finding our way around the software landscape that we build when programming.
Probably one of the most incredible new debug features in VS.NET is the bookmark. For anyone who has had to create special 'comments' in order to find a passage of code buried in a long stream of programming consciousness will soon value this tool. The whole concept of bookmarking a web address, a favorite place to return, has been a long accepted and expected feature in any browser.
To add a bookmark, select the Edit menu then Bookmarks to display the submenu options.
The Bookmark tools can also be found on the Text Editor toolbar.
To demonstrate bookmarks use, we'll add some to our code. Open the code pane by double clicking the Calculate button in our Life Insurance Calculator application. At the top of the Click event section, we'll place a bookmark on the variable declarations. Place your cursor on the
' The local calculation variables line and either click the Toggle Bookmark button or you can press CTRL+K twice. The bookmark indicator will appear in the border to the left.
Now scroll down a bit and find the
' Round the Salary . . . portion of the code and add a bookmark here as well. Have fun, set a few more bookmarks wherever you wish! These are now set in your code as temporary bookmarks.
To return to a temporary bookmark, click the Next Bookmark or Previous Bookmark buttons on the toolbar. To use the keyboard shortcuts, you can press CTRL+K and then CTRL+N for the next bookmark, or CTRL+K and then CTRL+P for the previous bookmark.
Another way to return to a location is by using the Navigate Backward and Navigate Forward buttons on the Standard toolbar.
To remove a single bookmark, click the Toggle Bookmark button again, or press CTRL+K twice again while on the line with the bookmark.
To erase all bookmarks, click the Clear Bookmarks button or press CTRL+K and then CTRL+L.
The Debug and Trace classes provide information about the performance of an application. The messages produced by these classes appear in the Output window of the VS.NET IDE by default. You can use the Trace and the Debug classes separately or together in the same application, but the solution configuration of a project will determine when the messages are actually written to the Output window. When a project is in a Debug Solution Configuration, both classes will output messages. A project with a Release Solution Configuration only generates output from a Trace class.
This is a good time to verify the current solution configuration.
- Open or locate the Solution Explorer window (CTRL+ALT+L).
- Right-click the project that is currently under development to open the menu. In our case that will be the LifeInsCalc project.
- Click the Properties option to open the property page.
- In the left pane of the property page, open the Configuration folder.
- Make certain that the arrow points to Debugging.
- Above the Configuration folder, in the Configuration drop-down list box, click Active (Debug) or Debug, and then click OK.
The Debug and Trace classes share most of the same methods in producing output, like WriteLine and Assert. Let's take a look at a few methods and demonstrate their usefulness.
Double-click the btnCalculate button control on our Life Insurance Cost Calculator form. Add this code to the end of the Click event:
Debug.WriteLine("Debug Information - Starting") Debug.Indent() Debug.WriteLine("The rounded salary is " & RoundSalary, "Field") Debug.Unindent() Debug.Assert(RoundSalary <> 0.0, "No Salary to Calculate Insurance.") Debug.WriteLine("Debug Information - Ending")
This is just a simple example to help us examine a few Debug class methods. The WriteLine method outputs a message into the Output window. The Write method would do the same thing, but a carriage return is included in the WriteLine method, so it can be more convenient for this type of situation.
The Indent method will simply indent any subsequent messages in the Output window. Conversely, the Unindent method will remove the indentation. This is a good way to visually group sets of messages when using several types of methods together.
The Assert method evaluates the expression in the first argument and only if it is false does the message in the second argument get sent to the Output window. In addition, an Assertion Failed modal dialog box appears with the message, the project name, and the Debug.Assert statement number. The dialog box also includes three command buttons:
- Abort: The application stops running.
- Retry: The application enters debug mode.
- Ignore: The application proceeds.
The user must click one of these buttons before the application can continue.
Before running the application, press CTRL+ALT+O to make sure that the Output window is open. Now click the Run button on the toolbar or hit the F5 key to run the application to test it with this new code. Select the Calculate button and observe the messages in the Output window.
Debug Information - Starting
Field: The rounded salary is 0
---- DEBUG ASSERTION FAILED ----
---- Assert Short Message ----
No Salary to Calculate Insurance.
---- Assert Long Message ----
and so on . . .
The Output window displayed our messages as defined and the Assert method should have opened a modal dialog window with our message about there being no salary to calculate. Select the Ignore option to close this window and continue.
As evidenced by this simple demonstration of the new and enhanced tools available in Visual Studio .NET, the possibilities for debugging VB.NET applications are endless. Aside from a few changes to terminology and menu item locations, nearly all of the familiar and time-tested debugging tools VB programmers have come to depend on remain intact. In addition, several new features have been added to help reduce or eliminate the need for common tricks used by many developers, and to open the door for development of automated debugging tools.