Sams Teach Yourself .Net in 21 Days
- Table of Contents
- Copyright
- About the Author
- About the Technical Editor
- Acknowledgments
- We Want to Hear from You
- Introduction
- Week 1: At a Glance
- Day 1. Introduction to the Microsoft .NET Framework
- Day 2. Introduction to Visual Studio .NET
- Day 3. Writing Windows Forms Applications
- Day 4. Deploying Windows Forms Applications
- Day 5. Writing ASP.NET Applications
- Day 6. Deploying ASP.NET Applications
- Day 7. Exceptions, Debugging, and Tracing
- Why Errors Happen
- Understanding Exceptions in .NET
- Using Structured Exception Handling
- Using Visual Studio .NET to Debug Applications
- Summary
- Q&A
- Quiz
- Exercises
- Week 1. In Review
- Week 2: At a Glance
- Day 8. Core Language Concepts in Visual Basic .NET and C#
- Day 9. Using Namespaces in .NET
- Day 10. Accessing Data with ADO.NET
- Day 11. Understanding Visual Database Tools
- Day 12. Accessing XML in .NET
- Day 13. XML Web Services in .NET
- Day 14. Components and .NET
- Week 2. In Review
- Week 3: At a Glance
- Day 15. Writing International Applications
- Day 16. Using Macros in Visual Studio .NET
- Day 17. Automating Visual Studio .NET
- Day 18. Using Crystal Reports
- Day 19. Understanding Microsoft Application Center Test
- Day 20. Using Visual SourceSafe
- Day 21. Object Role Modeling with Visio
- Week 3. In Review
Using Visual Studio .NET to Debug Applications
Visual Studio .NET and the .NET Framework give you all the tools you need to debug your applications while you're designing them. In this section, you learn how to use the Debug object on the FCL. You also learn how to use all the output windows in Visual Studio .NET to track exactly what's happening to your application when it's running.
Understanding the Debug Class
The Debug class is specifically used to aid you in determining the runtime state of your code via user-defined output and alerts. At design time, you use the properties and methods of the Debug class to write debug code. The Debug class uses one or more listeners to output debug information to a stream. The default output stream in Visual Studio .NET is the Output window. So, for example, any time you use the Debug.Writeline method in a Windows Form or ASP.NET application, the data is sent to the Output window by default. If you want the stream to go somewhere other than the Output window, you can add a new listener to the Listener collection and direct the output elsewhere, such as an event log or a text file.
In Visual Basic 6, you used the Debug.Print statement all over your code to output specific information about variable values or message data. The Debug class takes this a step further and gives you a full range of capability for outputting debug information. Before we get into any code, let's examine the properties and methods of the Debug class as listed in Tables 7.2 and 7.3, respectively.
Table 7.2. Properties of the Debug Class
|
Property |
Description |
|
AutoFlush |
Gets or sets a value indicating whether Flush should be called on the listeners after every write |
|
IndentLevel |
Gets or sets the indent level |
|
IndentSize |
Gets or sets the number of spaces in an indent |
|
Listeners |
Gets the collection of listeners that is monitoring the debug output |
Table 7.3. Methods of the Debug Class
|
Method |
Description |
|
Assert |
Checks for a condition and displays a message if the condition is false |
|
Close |
Flushes the output buffer and then closes the listeners |
|
Fail |
Emits an error message |
|
Flush |
Flushes the output buffer and causes buffered data to write to the Listeners collection |
|
Indent |
Increases the current IndentLevel by one |
|
Unindent |
Decreases the current IndentLevel by one |
|
Write |
Writes information about the debug to the trace listeners in the Listeners collection |
|
WriteIf |
Writes information about the debug to the trace listeners in the Listeners collection if a condition is true |
|
WriteLine |
Writes information about the debug to the trace listeners in the Listeners collection and adds a linefeed character to the end of the output |
|
WriteLineIf |
Writes information about the debug to the trace listeners in the Listeners collection if a condition is true and adds a linefeed character to the end of the output |
As you can see, there are many more options available to you with the Debug class than there were with the Visual Basic 6 Debug object.
To see the Debug class in action, you're going to modify the default form in the Exceptions project you started earlier in the day. Add a new Button to the default form1 and set the Text property to "Write Debug Information". For the code-behind for the new button, enter the code in Listing 7.4, which uses some of the properties and methods of the Debug class.
Example 7.4. Using the Debug Class to Output Debug Information
Private Sub Button3_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button3.Click
' Declare a generic variable for testing
Dim X As Integer = 5
' Create a new file stream
Dim fOut As Stream = File.Create("C:\ConsoleOutput.txt")
' Create a new listener using the file stream as the output
Dim textListener As New TextWriterTraceListener(fOut)
' Add the listener to the Listeners collection
Debug.Listeners.Add(textListener)
Debug.WriteLine("This is going to a text file")
' Increase the Indent level on the output by 1
Debug.Indent()
Debug.WriteLine(Date.Now)
Debug.WriteLine("The date was just sent to the file")
' Use the WriteLineIf to test for a value of False
Debug.WriteLineIf(X = 7, "X = 7")
' Use the WriteLineIf to test for a value of True
Debug.WriteLineIf(X = 5, "X = 5")
' Flush the listener out
textListener.Flush()
End Sub
private void button3_Click(object sender, System.EventArgs e)
{
int x = 5;
System.IO.Stream fOut = System.IO.File.Create(@"C:\csConsoleOut.txt");
System.Diagnostics.TextWriterTraceListener textListener = new
System.Diagnostics.TextWriterTraceListener(fOut);
System.Diagnostics.Debug.Listeners.Add(textListener);
System.Diagnostics.Debug.WriteLine("This is going to a text file");
System.Diagnostics.Debug.Indent();
System.Diagnostics.Debug.WriteLine(System.DateTime.Now);
System.Diagnostics.Debug.WriteLine("The date was sent to the text file");
System.Diagnostics.Debug.WriteLineIf(x == 7, "X = 7");
System.Diagnostics.Debug.WriteLineIf(x == 5, "X = 5");
textListener.Flush();
}
When you run the project, the default Output window displays the following:
This is going to a text file 7/21/2002 3:03:30 PM The date was just sent to the file X = 5
If you look at your C drive, you'll find the output text for the custom listener. Figure 7.6 shows what it should look like (other than the date and time being different).
Figure 7.6 Text file output of Listing 7.5.
So, to break down what happened in the code:
-
Created a new file on the file system called ConsoleOutput.txt
-
Created a new TextWriterTraceListener object, and set its output to the ConsoleOutput.txt file you created
-
Added the new listener to the Listeners collection
-
Wrote out some debug information using the Debug.Writeline, Debug.Indent, and Debug.WritelineIf methods
-
Called the Flush method of your custom listener to flush the buffer of debug data, writing it to the file
There you have it: a new way to keep track of what's happening during the execution of your application without having to use intrusive MessageBox.Show prompts or stopping code execution with breakpoints.
Using Breakpoints to Debug Code
Visual Studio .NET enables you to set predetermined points in your code that cause execution to stop so that you can examine the state of the application. Breakpoints stop your code only when you're running it in the Visual Studio .NET IDE; after the code is deployed, breakpoints are ignored.
By default, a breakpoint stops executing code at the exact line at which it's set. The condition that causes the application to break can be set for a number of circumstances, which we look at in a second. Breakpoints can be set only on a line of executable code, which doesn't include empty lines or variable declarations that neither instantiate an object nor assign a value to the newly declared variable. You set breakpoints to examine what's going on with any running object in your code. They enable you to see what state variables are in, what information objects are holding, and what threads your application is running on. After you set a breakpoint and your code stops executing, you can modify certain variables to see how your code reacts—thus minimizing that chance that errors will occur after your application is deployed.
Breakpoints can be set in the following five ways:
-
Use the mouse to click on the margin to the left of the line where the breakpoint should be set
-
Place the cursor on the line where the breakpoint should occur and press F9
-
Select New Breakpoint from the Debug menu
-
Right-click the line of code where the breakpoint should be set and select New Breakpoint or Insert Breakpoint on the contextual menu
-
Place the cursor on the line where the breakpoint should occur and press Ctrl+B to bring up the Breakpoint properties dialog and set the breakpoint
After a breakpoint is set, the line of code on which you set the code to break is easily identified by a burgundy-colored line that spans the line. Figure 7.7 shows this in action.
Figure 7.7 Setting a breakpoint.
Breakpoints can also be either removed permanently, or disabled so that they don't break the running code but can be easily enabled at any time.
To remove breakpoints, you can
-
Use the mouse to click on the margin to the left of the line where the breakpoint is currently set
-
Place the cursor on the line where the breakpoint occurs and press F9
-
Right-click the line of code where the breakpoint should be removed and click Remove Breakpoint from the context menu
-
Select Clear All Breakpoints from the Debug menu
To disable/enable breakpoints, you can
-
Right-click the line of code where the breakpoint should be disabled (enabled) and select Disable (Enable) Breakpoint from the contextual menu
-
Select Disable (Enable) All Breakpoints from the Debug menu
After you set a breakpoint, you might or might not want the code to break automatically—you might want to set some sort of condition. You can do so with the Breakpoint Properties dialog box, which you can open by right-clicking the breakpoint that you've set and selecting Properties from the contextual menu. By default, an enabled breakpoint always causes the application to break. Figure 7.8 shows the Breakpoint Properties dialog box.
Figure 7.8 The Breakpoint Properties dialog box.
To set a condition for when the Breakpoint will break, use the Condition button or Hit Count button.
The Condition Button Details
Breakpoint conditions cause the execution to break only when the condition has been met. The condition can be any expression that can be evaluated in the current scope of the breakpoint. The condition is further evaluated for either being True or having changed since the breakpoint condition was last evaluated. When the expression or state condition is met, the breakpoint causes code execution to break on that line. Figure 7.9 is an example of setting a condition to break the code when the variable X is equal to 22. If the variable X never contains the value of 22, the condition is never met and the code execution never breaks.
Figure 7.9 Setting a condition for a breakpoint.
The Hit Count Button Details
In addition to (or instead of) specifying a condition that breaks execution, a breakpoint can be set to break only when the hit count expression is True. The expression must consist of one of the following options:
- Break Always
- Break when the hit count is equal to
- Break when the hit count is a multiple of
- Break when the hit count is greater than or equal to
The Breakpoint Hit Count dialog enables you to set the expression and related value. Figure 7.10 shows a hit count condition being set.
Figure 7.10 Setting a hit count expression.
Effective use of breakpoints can accelerate debugging time because it allows your application to run normally and stop at the exact line of code (and with the proper conditions) that you're trying to debug. Therefore, time isn't wasted needlessly stepping through code and reviewing the state of every variable in the application. After you hit a breakpoint, you're in break mode, and you can use the Debug windows to examine the state of your application.
Using the Debug Windows in Break Mode
When your application hits a breakpoint, several very useful debug windows become available that enable you to view and update variables, run local methods, and evaluate expressions. Each of the Debug windows is available only while in break mode, and can be opened by selecting Debug, Windows from the main menu.
Now let's go through each of the Debug windows and describe how you can use each one to debug your application effectively.
Using the Command Window
The Command window is perhaps the most valuable Debug window that Visual Studio .NET provides. The command window has two modes: command mode and immediate.
When the Command window is in immediate mode, you can evaluate an expression, execute a statement, or print any variable's value in the current scope.
By default, the command window is in command mode. To determine which mode the Command window is currently in, use the following table:
|
Caption |
Command Prompt |
|
|
Command Mode |
Command Window |
> |
|
Immediate Mode |
Command Window - Immediate |
None |
To change the command window to immediate mode, simply type immed at the command prompt and press the Enter key. To change to command mode, type >cmd at the command prompt and press the Enter key.
The command window is available only while in break mode, and commands are evaluated in the language of the current scope. The Command window allows one command per line and doesn't allow you to modify and run previous commands as was the case with Visual Basic 6.0. It does, however, enable you to quickly recall previous commands by pressing the up and down arrows. Figure 7.11 gives you an idea of what kind of commands you can use in the Command window.
Figure 7.11 Using the Command window.
Using the This/Me Window
The This/Me window enables you to view and modify data members of the object that's within the current scope. If code is currently being executed in something other than an object (BAS module in Visual Basic), the This/Me window is empty. The window is named This in C# and Me in Visual Basic.
By double-clicking in the value cell of the data member, you can edit the current value of that member and it's changed for the existing execution scope. Figure 7.12 shows the Me window.
Figure 7.12 The Me window in action.
Using the Autos Window
The Autos window is similar to the This/Me window in that you can view and update application variables in the current scope. The difference is that the Autos window displays variables used in the current statement and the previous statement for C++ and C#, or the current statement and the three statements on either side of the current statement for Visual Basic. Only primitive values can be changed in the Autos window. Changing values is done in the same fashion as in the This/Me window: Double-click on the value cell of the variable to change and type the updated value.
Using the Locals Window
The Locals window is similar to the Autos window in every way except the variables that it displays. The Locals window displays all variables that are local to the current scope, whereas the Autos window displays a subset of those variables that appear around the current statement.
Using the Watch Window
The Watch window is similar to the Local and Autos window in its utility. Once again, the difference is in the variables it displays. By default, the Watch window doesn't display any variables. You're actually setting a variable or expression to watch. When a watch condition is met, the execution stops and your application enters break mode. Any variable or expression that you want to continuously review can be added to the Watch window by right-clicking on the item you want to add, and selecting Add Watch from the contextual menu. You may also type in the empty Name column of the Watch window to add your expression. To delete a watch, right-click on the watch to delete and click Delete Watch from the contextual menu, or select the watch to delete and press the Delete key.
Using the QuickWatch Dialog Box
The QuickWatch dialog box is similar to the Watch window, but is most useful in viewing or updating variable values only one time. By typing in the expression to evaluate and clicking Recalculate, you can view or change (primitives only) the value of the expression.
Using the Call Stack Window
The Call Stack window gives you a view into the call stack of the application. From this window, you can change the current scope of the application by double-clicking the item that you want to change to in the call stack, or by right-clicking the item and selecting Switch to Frame from the contextual menu.
Another interesting capability of the Call Stack window is that you can right-click any item in the call stack, and select Run to Cursor from the contextual menu. Doing so runs the application until it gets to the point in the call stack at which you clicked. This capability makes it very easy to maneuver through the application when you're buried in the call stack.
The information displayed in the Call Stack window can be turned on or off by right-clicking in the Call Stack window, and then choosing the appropriate column in the contextual menu to toggle. Your choices for the call stack are
- Module names
- Parameter types
- Parameter names
- Parameter values
- Line numbers
- Byte offsets
- Memory window
Using the Disassembly Window
The Disassembly window displays the native assembler code of the current scope. For you hardcore programmers who like to know everything that's going on under the hood, the Disassembly window enables you to view processor-level commands for your application. Not only can you view your own code, but you can also view the assembly code running in your .NET debugger. The window enables you to show or hide information such as the memory address location relative to the beginning of the function, source code, code bytes (actual byte code used by the processor to execute the instruction), symbol names (associated with the memory address), and line numbers. The Disassembly window gives you the following functionality:
-
Jump to the source code associated with the assembly instructions displayed
-
Add a quick watch based on an assembly instruction
-
Insert a breakpoint
-
Jump to the next line of execution (in the event that you've wandered off in the code)
-
Run to cursor
-
Set next statement
In addition to opening the disassembly window by selecting Window, Disassembly from the Debug menu, you can right-click in the source code window and choose Go to Disassembly from the contextual menu.
Using the Threads Window
The Threads window displays information about all threads running in the debugger. Using this window, you can see which threads are running, their scheduling priority, and current state of the thread. By right-clicking in the Threads window and selecting Freeze, you can suspend activation of a running thread. You can also select Thaw on a suspended thread to resume normal activation. You can view the code associated with a thread by right-clicking on the thread and selecting Switch to Thread from the contextual menu. The Threads window is most useful when you're debugging multithreaded applications.
Using the Modules Window
The Modules window displays information regarding all currently loaded program files. The information displayed is
- Name of the module (that is, .dll or .exe filename)
- Address in memory where the module is located (starts)
- Path of the module in the file system
- Order in which the modules were loaded
- Version number of the module that's loaded
- The ProgramID and name of the PE that owns the module
- Time that the module was created
- Whether or not debug information has been loaded for the module (symbols loaded means you can step through source code to debug; otherwise, you can only step through disassembly)
In addition to the information in the preceding list, the Modules window can be used to tune your application's performance. A module that appears in the Modules window with a red exclamation point and asterisk next to it has been loaded at a relocated base address because another module was already using its base address space. When this occurs, the processor must spend extra clock cycles to point itself to the adjusted address, which causes decreased performance. When this occurs, consider changing the base address of the module that has been relocated.
Now that you understand what the Debug windows can offer you on your road to perfect code, let's take a look at how you can step through code after you're actually in break mode.
Stepping Through Code
While in break mode, the Visual Studio .NET IDE enables you to step through the code to see the actual execution path through the application. This is extremely useful because it gives you a chance to check for logic errors and view variable contents throughout execution. To step though code, you can use the Debugging toolbar shown in Figure 7.13, or you can right-click anywhere in the code window and select any of the options discussed next.
Figure 7.13 The Debugging toolbar.
Step Into
While stepping through code, it will eventually happen that the next line of executable code is a property or method call. When you reach this point using the Step Into option, the debugger goes into the property or method call that's called.
Step Over
If you want to ignore what code is running inside a property or method call, it's useful to Step Over the call. In this way, the code that's called is run normally, and you re-enter break mode when execution gets to the next line of code after the call that you stepped over.
Step Out
If you find yourself in a property or method call that you don't care to be in and don't want to step through each line of code to get out, use Step Out. Doing so causes the application to execute normally, and re-enter break mode on the next line of code after the call of the property or method that you were in when you stepped out.
Run to Cursor
Run to Cursor enables you to choose a position at some point ahead of the current line of execution at which the application should re-enter break mode. That is to say, the application executes normally until it reaches the point of execution.
Set Next Statement
Sometimes you might find that you don't want to follow the standard execution path of the application. Perhaps you want to go back in the code execution or skip a line. This can be done by using Set Next Statement.
Continue
Continue enables you to exit break mode and execute the rest of the application as normal (until break mode is re-entered later).
Stop Debugging
Use Stop Debugging to end the debug session as well as close the application. When this occurs, the application stops, and the IDE is now in design time.
Restart
While in break mode, you can stop and restart the application. On restart, the application enters break mode at the first line of executable code in your application.
Detach All
Detach All detaches the debugger from all running threads currently used by the debugger. That means the threads continue to run as normal, without the use of the debugger.
Hovering
The debugger also enables you to view values of expressions and object types by hovering the cursor over the selected code. For example, if you have a local variable, i, whose value is 10, you can hover the cursor over the i and view the contents (i = 10). You can also highlight a line of code, say i = 50, and view the results of the expression (i = 50 = False). By hovering over objects, you can view the type name of the object or view the function prototype of procedure calls.
Edit and Continue - Not Supported
For those programmers familiar with Visual Basic 6.0 (and earlier), it will come as sad news to discover that Visual Studio .NET doesn't support the capability to edit and continue. Code can no longer be changed while the debugger is in break mode, and execute as normal using the newly entered code. You can change code while in break mode, but an attempt to run the code any further results in a dialog box that asks whether you want to restart the application.
Keyboard Shortcuts Visual Studio and Visual Basic Versions
Table 7.4 gives you an idea of what shortcuts are available in Visual Basic .NET and C#, and how they compare to Visual Basic 6 debugging shortcuts.
Table 7.4. Keyboard Shortcuts in C# and Visual Basic .NET Compared to Visual Basic 6
|
Keyboard Shortcut Layouts |
Debug Menu |
Context Menu |
||
|
Default |
VB6 |
|||
|
Continue |
F5 |
F5 |
Continue |
|
|
Break All |
Ctrl+Alt+Break |
Ctrl+Break |
Break All |
|
|
Stop Debugging |
Shift+F5 |
Stop Debugging |
||
|
Restart |
Ctrl+Shift+F5 |
Shift+F5 |
Restart |
|
|
Detach All |
Detach All |
|||
|
Step Into |
F11 |
F8 |
Step Into |
|
|
Step Over |
F10 |
Shift F8 |
Step Over |
|
|
Step Out |
Shift+F11 |
Ctrl+Shift+F8 |
Step Out |
|
|
Run to Cursor |
Run to Cursor |
|||
|
Set Next Statement |
Set Next Statement |
Summary | Next Section

Account Sign In
View your cart