The Visual Studio Debugger


The debugger built into Visual Studio 2005 is one of the largest and most complex tools in the IDE. With such a large feature-set area, we cannot cover every scenario you will encounter. However, we hope to expose the most commonly applicable features in this section. We will continue to work with the customer profile scenario for our examples.

The Debug Menu and Toolbar

The Debug menu and related toolbar provide your first-level access to starting debug sessions, stepping into code, managing breakpoints, and accessing the many features of debugging with Visual Studio. There are two states to the debug window: at rest and debug mode. In the at-rest state, the Debug menu allows you to start a debug session, attach code to a running process, or access some of the many debug windows. Figure 9.11 shows the menu in this state.

Figure 9.11. The Debug menu at rest.


When the debugger is engaged and you are working through a debug session, the state of the Debug menu changes. It now provides a number of additional options to those provided by the at-rest state. These options include those designed to move through the code, restart the session, and access even more debug-related windows. Figure 9.12 shows the Debug menu during a debug session.

Figure 9.12. The Debug menu during a debug session.


Let's look at the many options provided by the Debug menu. Table 9.1 presents all the items available from the toolbar, whether an at-rest option or one that is available only during a debug session. When reading through the table, refer to the preceding figures to get context on any given item.

Table 9.1. The Debug Menu Items

Menu Item

Description

Windows, Breakpoints

Opens the Breakpoints window in the IDE. This window provides access to all the breakpoints in the option solution.

Windows, Output

Shows the Output window in the IDE. The Output window is a running log of the many messages that are emitted by the IDE, the compiler, and the debugger. This information can be useful during a debug session.

Windows, Immediate

Opens the Immediate window in the IDE. This window allows you to execute commands. For example, during design, you can call your methods directly from the Immediate window and have them enter breakpoints and so on.

Start Debugging, Continue

Either starts your application in debug mode or continues running the application if already in debug mode.

Start Without Debugging

Starts your application. However, it does not connect the debugger to the process. Therefore, this more closely represents what users would see when they run your application.

Attach to Process

Allows you to attach the debugger (and your code) to a running process (executable).

Exceptions

Opens the Exceptions option dialog box. This dialog box allows you to choose how the debugger breaks on any given exception.

Step Into

Starts the application in debug mode when your application is in design mode. For most projects, clicking the Step Into command invokes the debugger on the first executing line of the application. In this way, you can step into the application from the first line.

If you are in a debug session, Step Into advances the debugger a line. If you choose to "step into" a function, the debugger will do so line-by-line.

Step Over

Functions the same as Step Into with one major difference: If you choose to "step over" a function, the line calling the function will be executed (along with the function), and the debugger will set the next line (after the function call) as the next line to be debugged.

Toggle Breakpoint

Toggles the breakpoint on the current line of code. If the breakpoint is set, toggling it will remove it. If it is not set, toggling will set it.

New Breakpoint, Break at Function

Brings up the New Breakpoint dialog box. This dialog box allows you to indicate where in a given function the code should break.

Delete All Breakpoints

Removes all breakpoints from your solution.

Disable All Breakpoints

Disables breakpoints in the solution without deleting them. You can also disable individual breakpoints. This capability is very useful if you want to keep the breakpoints around for later but simply don't want to hit that one at the moment.

Enable All Breakpoints

Enables all breakpoints that have been disabled due to a call to Disable All Breakpoints.

Break All

Allows you to break the application into the debugger manually (without having to hit a breakpoint) during a debug session. This capability is useful to gain access to the debug information such as watch windows.

Stop Debugging

Terminates the debugging session. It also terminates the process you are debugging, provided that process was started by Visual Studio.

Detach All

Detaches the debugger from executing process. This allows your application to continue running after the debugger is through with it.

Terminate All

Stops debugging and terminates all processes to which you are attached.

Restart

Stops the debugging session and restarts it. Similar to clicking both Stop Debugging and Start Debugging in sequence.

Step Out

Tells the debugger to execute the current function and then break back into debugging after the function has finished. This capability is useful if you step into a function but then want to have that function just execute and yet return you to debug mode when it is complete.

QuickWatch

Brings up the QuickWatch window when the debugger is in break mode. The QuickWatch window shows one variable or expression you are watching and its value. QuickWatch's usefulness has been replaced by the new DataTips (more on this to come).

Windows, Script Editor

Opens the script editor. This feature is useful for debugging client- and server-side script.

Windows, Watch

Opens one of many possible watch windows in the IDE. Watch windows represent items and expressions you are keeping a close eye on through the debug session.

Windows, Autos

Opens the Autos window. This window shows variables (and their value) in the current line of code and the prior line of code.

Windows, Locals

Opens the Locals window in the IDE. This window shows variables in the local scope (function).

Windows, Call Stack

Shows the list of functions that are on the stack. Also indicates the current stack frame (function). This selected item is what defines the content from the Locals, watch, and Autos windows.

Windows, Threads

Shows the Threads window in the IDE. From here, you can view and control the threads in the application you are debugging.

Windows, Modules

Shows the Modules window in the IDE. This window lists the DLLs and EXEs used by your application.

Windows, Processes

Shows the Processes window in the IDE. This window lists the processes to which the debug session is attached.

Windows, Memory

Opens the Memory window for a view at the memory used by your application. This is valid only when address-level debugging is enabled from the Options dialog box.

Windows, Disassembly

Opens the Disassembly window. This window shows the assembly code corresponding to the compiler instructions. This is valid only when address-level debugging is enabled from the Options dialog box.

Windows, Registers

Opens the Registers window so that you can view register values change as you step through code. This is valid only when address-level debugging is enabled from the Options dialog box.


The Debug Toolbar

The Debug toolbar provides quick access to some of the key items available on the Debug menu. From here, you can manage your debug session. For example, you can start or continue a debug session, stop an executing session, step through lines of code, and so on.

Figure 9.13 presents the Debug toolbar during an active debug session. In design mode, the Continue button would read Start Debugging, and a number of these items would be disabled. We have added callouts for each item on the toolbar. You can cross-reference these callouts back to Table 9.1 for further information.

Figure 9.13. The Debug toolbar during break mode.


Note

In Figure 9.13 the Breakpoints window icon on the right of the figure with the callout "Debug windows" actually is a drop-down menu. This menu provides access to the many debug windows that are available to developers.


Debug Options

You can control the many debugging options in Visual Studio through the Options dialog box. The Debugging node on the options tree provides access to these debugging switches. Figure 9.14 shows the general debugging settings.

Figure 9.14. The Debug Options dialog box.


The general settings list provides access to turn on and off many debugging options. These options include all of the following:

  • Turn on and off breakpoint filters

  • Enable or disable the warning dialog box associated with clearing all breakpoints

  • Turn on or off the Exception Assistant

  • Enable or disable just-my-code debugging

  • Require source code to exactly match that being debugged (or not)

  • And many more

There are additional debug option dialog boxes, which provide access to more debug settings. For instance, you can control how Edit and Continue works (you can also turn off this feature). There are settings for which type of code (Managed, Native, Script) is enabled for just-in-time debugging. You also have the option for using additional debug symbol files (.pdb and .dbg). These files can be helpful if you do not have the source code associated with a particular library you need to debug, such as Windows source or a third-party component.

These many options help you customize your debug experience. However, for this chapter, we are going to accept the default options and debug accordingly.

Stepping In, Out, and Over Code

Probably the most common debug operation for developers is stepping through their code line-by-line and examining the data emitted by the application and the debugger. Code stepping is just that, examining a line, executing the line, and examining the results (and then repeating the process over and over). Because this is such a dominant activity, becoming efficient with the step operations in Visual Studio is important for maximizing the use of your time during a debug session. Here, we will cover each of the stepping options and provide examples accordingly.

Beginning a Debug Session (Stepping into Code)

The Step Into command is available from both the Debug menu and toolbar (you can also press F11 as a shortcut). There are two behaviors commonly associated with this one command. The first is related to when you invoke the command for an application that is not currently running in debug mode. In this case, the application will be compiled, started, and the first line presented to you in the debug window for stepping purposes. This is, in essence, stepping into your application. Figure 9.15 shows a Windows form application in debug mode as the result of a call to Step Into.

Figure 9.15. Using Step Into to start an application.


Note

For web applications, using Step Into or Step Over does not work the same. Instead, your application simply runs in debug mode in the case of websites. The debugger does not break on the first line of your application. To do this, you must set a breakpoint or choose the Run To Cursor option (see the following section).


A call to the Step Over command (Debug menu, toolbar, or F10) while your application is at rest will result in the same behavior as Step Into. That is, your application (provided it is not a website) will be compiled and started in a debug session on the first line of code.

Run To Cursor

One of the more handy (and overlooked) features of the debug toolset is Run To Cursor. This feature works the way it sounds. You set your cursor position on some code and invoke the command. The application is compiled and run until it hits the line of code where your cursor is placed. At this point, the debugger breaks the application and presents the line of code for you to step through. This capability is especially handy because this is how many developers work. They are looking at a specific line (or lines) of code and want to debug this line. They do not need to start from the first line and often do not want to be bothered with breakpoints. The Run To Cursor feature is therefore an efficient means to get the debugger on the same page as you. Figure 9.16 shows this feature being invoked from the context menu.

Figure 9.16. Invoking the Run To Cursor command.


Run To Cursor works even if the user is required to activate some portion of the code prior to the code's reaching the cursor position. In this way, it really is an invisible, temporary breakpoint. For instance, in the example, users are presented with a default page. From here, they can select to edit their profiles as an option. If you set the Run To Cursor command on a line inside the edit profile screen, the debugger will still execute the application and wait until the users (testers or developers) invoke the given line of code.

Start Debugging

You can also start your debug session by selecting the Start Debugging option (green "play" arrow) from the Debug menu or toolbar (or F5). This starts a debug session but does not break into code unless an exception occurs or a breakpoint is encountered. This is a common operation for developers testing their code without wanting to walk through it or those who use a lot of breakpoints.

Break All

Finally, if your application is running and you want to enter break mode, you can do so at any time by invoking the Break All command from the Debug menu or toolbar (or Ctrl+Alt+Break). The Break All feature is represented by the pause icon. This stops your application wherever it is in execution and allows you to interrogate the debugger for information. The Break All command is especially useful if you need to break into a long-running process or loop.

Walking Through Your Code

During a debug session, you have basically three options for moving through your code. You can step into a line or function, step over a given function, and step out of a function. Let's look at each option.

Step Into

The Step Into command (F11) allows you to progress through your code one line at a time. Invoking this command will execute the current line of code and position your cursor on the next line to be executed. The important distinction between stepping into and other similar commands is how Step Into handles lines of code that contain function calls. If you are positioned on such a line, calling Step Into will take you to the first line inside the function (provided you have the appropriate debug symbols loaded).

As an example, look at Figure 9.17. It shows the sample web service making a call to the data access library's method named Customer.GetById. In this case, both projects are loaded in the solution; thus, you have access to their debug symbols. Therefore, a call to Step Into will result in your stepping into the first line of GetById.

Figure 9.17. Stepping into a line of code.


Figure 9.18 shows stepping into this function. Notice that you are now positioned to step line-by-line through this function. Of course, when you reach the end of this function, the debugger will return you to the next line in the calling function (line 23 in the web service depicted in Figure 9.17).

Figure 9.18. The results of stepping into a function.


Step Over

The Step Over command (F10) allows you to maintain focus on the current procedure without stepping into any methods called by it. That is, calling Step Over will execute line-by-line but will not take you into any function calls, constructors, or property calls.

As an example, consider Figure 9.17. Here, the debugger is positioned on the call to Customer.GetById. If you called the Step Over command, the GetById function would execute without your stepping through it. Instead, the next line to execute in step mode would be the line following the call to GetById. Of course, any exception thrown by the function you step over will result in the debugger breaking into your code as normal.

Step Out

The Step Out command (Shift+F11) is another useful tool. It allows you to tell the debugger to finish executing the current method you are debugging but return to break mode as soon as it is finished. This is a great tool when you get stuck in a long method you wished you had stepped over. In addition, you may step into a given function only to debug a portion of it and then want to step out.

As an example, refer again to Figure 9.18. Recall that you stepped into this method from the code in Figure 9.17. Suppose you start stepping through a couple of lines of code. After you take a look and verify that a connection is made to the database, you simply want to have the function complete and return to debugging back in the calling function (line 23 of Figure 9.17). To do so, you simply invoke Step Out.

Continuing Execution

When you are in a debug session, the Start Debugging (or Run) command changes to Continue. The Continue command is available when you are paused on a given line of code in the debugger. It enables you to let the application continue to run on its own without stepping through each line. For example, suppose you walked through the lines of code you wanted to see, and now you want to continue checking your application from a user's perspective. Using Continue, you tell the application and debugger to keep running until either an exception occurs or a breakpoint is hit.

Ending a Debug Session

You can end your debug session in few ways. One common method is to kill the currently executing application. This might be done by closing the browser window for a web application or clicking the Close (or X) button of a Windows application. Calls in your code that terminate your application will also end a debug session.

There are also a couple of options available to you from the Debug window. The Terminate All command kills all processes that the debugger is attached to and ends the debug session. There is also the Detach All option. Figure 9.19 shows both options in the toolbar. Detach All simply detaches the debugger from all running processes without terminating them. This capability can be useful if you've temporarily attached to a running process, debugged it, and want to leave it running.

Figure 9.19. Detaching from a process.


Indicating When to Break into Code

You control the debugger through breakpoints and tracepoints. With these, you can tell the debugger when you are interested in breaking into code or receiving information about your application. Breakpoints allow you to indicate when the debugger should stop on a specific line in your code. Tracepoints are new to Visual Studio 2005. They are a type of breakpoint that allows you to perform an action when a given line of your code is reached. This typically involves emitting data about your application to the output window. Mastering the use of breakpoints will reduce the time it takes to zero in on and fix issues with your code.

Setting a Breakpoint

The most common method of setting a breakpoint is to first find the line of code on which you want the debugger to stop. You then click in the code editor's indicator margin for the given line of code. Doing so will place a red circle in the indicator margin and highlight the line of code as red. Of course, these are the default colors; you can change the look of breakpoints in the Tools, Options dialog box under the Environment node, Fonts and Colors.

There are a few additional ways to set breakpoints. For instance, you can right-click a given line of code and choose Insert Breakpoint from the Breakpoint context menu. You can also choose New Breakpoint from the Debug menu (or press Ctrl+D, N). This option brings up the New Breakpoint dialog box in which you can set a function breakpoint.

Setting a Function Breakpoint

A function breakpoint is just a breakpoint that is set through the New Breakpoint dialog box. It is called a function breakpoint because it is typically set at the beginning of the function (but does not need to be). From the New Breakpoint dialog box, you can manually set the function on which you want to break, the line of code in the function, and even the character on the line.

If your cursor is on a function or on a call to a function when you invoke this dialog box, the name of the function will automatically be placed in the dialog box. You can also type a function name in the dialog box. Figure 9.20 shows the New Breakpoint dialog box in action. Notice that you can manually set the line and even the character on the line where the breakpoint should be placed.

Figure 9.20. The New Breakpoint dialog box.


Note

If you specify an overloaded function in the New Breakpoint dialog box, you must specify the actual function on which you want to break. You do so by indicating the correct parameter types for the given overload. For example, the current ShowCustomerDetails takes a customer ID as an int. If you had an overload that also looked up a customer by name (as a string), you would indicate this overload in the Function field as ShowCustomerDetails(string).


Recognizing the Many Breakpoints of Visual Studio

Visual Studio 2005 has a number of breakpoint icons. These icons allow you to easily recognize the type of breakpoint associated with a given line of code. For instance, a round, filled circle is a common breakpoint, whereas a round, hollow circle represents a common breakpoint that has been disabled. We've provided Table 9.2 for reference purposes. It shows some of the more common icons associated with breakpoints and presents a description of each.

Table 9.2. The Breakpoint Icons

Icon

Description

This icon indicates a standard, enabled breakpoint. When the debugger encounters this line of code, it will stop the application and break into debug mode.

This icon indicates a standard tracepoint. When the debugger hits this line of code, it will perform the action associated with the tracepoint.

The plus icon inside the breakpoint indicates an advanced breakpoint that contains a condition, hit count, or filter.

The plus icon inside the tracepoint indicates an advanced tracepoint that contains a condition, hit count, or filter.

An empty or hollow breakpoint indicates a disabled breakpoint. The breakpoint is still associated with the line of code. However, the debugger will not recognize the disabled breakpoint until it has been re-enabled.

Hollow icons are associated with types of breakpoint icons such as tracepoints, advanced items, and even breakpoint errors and warnings. In all conditions, the hollow icon indicates the item is disabled.

Represents a breakpoint warning indicating that a breakpoint cannot be set due to a temporary condition. This can be the result of debugging not being enabled for a website or debug symbols not being loaded. These icons are set by Visual Studio.

Represents a tracepoint warning (see preceding description).


Working with the Breakpoints Window

The Breakpoints window in Visual Studio provides a convenient way to organize and manage the many conditions on which you intend to break into the debugger. You access this window from the Debug menu or toolbar (or by pressing Ctrl+D, B). Figure 9.21 shows the Breakpoints menu inside Visual Studio.

Figure 9.21. The Breakpoints window.


The Breakpoints Window Toolbar

The Breakpoints window has its own toolbar that allows you to manage the breakpoints in the window. The commands available from the toolbar are described in detail in Table 9.3.

Table 9.3. The Breakpoints Window Toolbar

Item

Description

Brings up the new Breakpoints window, allowing you to set a breakpoint at a function.

Allows you to delete the selected breakpoint in the list.

Deletes all breakpoints in the window.

Toggles all breakpoints as either on or off. If all breakpoints are enabled, clicking this icon allows you to keep the breakpoints but turn them all off as a group. If all breakpoints are disabled, this command will enable them as a group.

Allows you to go to the source code associated with the selected breakpoint.

Allows you to go to the disassembly information associated with the selected breakpoint.

Allows you to choose which columns you want to view in the Breakpoints window. Each column provides information about a given breakpoint. For example, you can see information on the condition associated with each breakpoint, the filename, the function, the filter, the process, and so on.


Managing Each Individual Breakpoint

The Breakpoints window also gives you access to each individual breakpoint. It serves as a launching point for setting the many options associated with a breakpoint. For example, you can disable a single breakpoint by toggling the check box associated with the breakpoint in the list. In addition, you can set the many properties and conditions associated with a breakpoint. Figure 9.22 shows both a disabled tracepoint and the context menu associated with an individual breakpoint.

Figure 9.22. Managing an individual breakpoint.


Notice that from this context menu, you can delete the breakpoint and navigate to its related source code. More important, however, is the access to setting the conditions and filters associated with the breakpoint. We will cover using each of these in the next section.

Breaking Based on Conditions

Often, setting a simple breakpoint is not sufficient (or efficient). For instance, if you are looking for a particular condition to be true in your codea condition that seems to be causing an exceptionthen you would prefer to break based on that condition. This saves the time of constantly breaking into a function only to examine a few data points and determine that you have not hit your condition. The following sections highlight the conditional options available for breakpoints.

Setting a Breakpoint Condition

A breakpoint condition allows you to break into the debugger or perform an action (tracepoint) when a specific condition is either evaluated as true or has changed. Often, you know that the bug you are working on occurs only based on a very specific condition. Breakpoint conditions are the perfect answer for finding that intermittent bug.

To set a condition, you select the breakpoint on which you want to apply a condition. You then choose the Condition option from the right-click context menu. This will bring up the Breakpoint Condition dialog box, as shown in Figure 9.23. Notice that when setting the condition, you have access to IntelliSense (you can invoke IntelliSense either when you click a dot or press Ctrl+Space).

Figure 9.23. Setting a breakpoint condition.


When you set a condition, you have two options: Is True and Has Changed. The Is True option allows you to set a Boolean condition that, when evaluated to true, results in the debugger's breaking into the given line of code.

For an example, refer to the sample application. Suppose that you are notified of an error that happens only for a specific customer. You might go to the Customer class and set a breakpoint inside the GetCustomerProfile function. You might then add the Is True condition, customerId=1234, to the breakpoint (where customerId is the parameter to the function). This will tell the debugger not to stop on this line of code unless this condition is met. Figure 9.24 shows this condition in the dialog box. It also presents the two options available for conditions.

Figure 9.24. The Breakpoint Condition dialog box.


The Has Changed option tells the debugger to break when the value of an expression changes. The first pass through your code sets the value for the first evaluation. If the value changes after that, the debugger will break on a given line. This capability can be useful when you have fields or properties with initial values and you want to track when those values are being changed. In addition, Has Changed can be useful in looping and if…then scenarios where you are interested in only whether the results of your code changed a particular value.

Tip

Your breakpoint information is persisted between debug sessions. That is, when you close Visual Studio for the day, your breakpoints are still there when you return. This validates the time you might spend setting some sophisticated debugging options. They can remain in your application and turned on and off as required.


Setting a Breakpoint Filter

Breakpoint filters allow you to specify a specific machine, process, or thread on which you want to break. For instance, if your error condition seems to happen only on a certain machine or within a certain process, then you can debug this condition specifically with a filter. Filters are most useful in complex debugging scenarios where your application is highly distributed.

To use this feature, you can specify the machine by name, the process by name or ID, or the thread by name or ID. You can also use specify combinations with & (and), || (or), and ! (not). This allows you to get to a specific thread on a specific process on a certain machine. Figure 9.25 shows the dialog box in which you set breakpoint filters.

Figure 9.25. The Breakpoint Filter dialog box.


Using a Hit Count with a Breakpoint

Using the Hit Count command, you can tell the debugger that you want to break when a given line of code is reached a number of times. This feature is useful only if you cannot set a better condition and know that when you pass over your code a certain number of times something bad happens. However, the Hit Count option might be more useful in tracepoint scenarios where you are emitting data about what is happening in your code.

Figure 9.26 shows the Breakpoint Hit Count dialog box. Notice that this screenshot was taken during an active debug session. You can add any of these conditions to breakpoints during an active debug session. In addition, notice that the current hit count is set to one (1). You have the option to click the Reset button and turn the hit count back to zero and continue debugging from that point.

Figure 9.26. Setting a breakpoint hit count.


This dialog box also provides a few options for setting the actual hit count. In the drop-down list under When the Breakpoint Is Hit, the following options are available:

  • Break Always (the default and does not invoke the hit count option)

  • 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

Tip

You can combine all the breakpoint conditions we've discussed to create even more specific conditions for your breakpoints.


Working with Tracepoints (When Hit…)

Tracepoints allow you to emit data to the Output window or run a Visual Studio macro when a specific breakpoint is hit. This capability can be very useful if you want to keep a running log of what is happening as your application runs in debug mode. You can then review this log to get valuable information about specific conditions and order of execution when an exception is thrown.

You can set tracepoints explicitly by right-clicking a line of code and choosing Insert Tracepoint from the Breakpoint menu. In addition, selecting the When Hit command from the context menu for a breakpoint (in the Breakpoints window) will bring up a tracepoint dialog box, which is titled When Breakpoint Is Hit, as shown in Figure 9.27.

Figure 9.27. Setting a tracepoint.


The options available for the When Breakpoint Is Hit dialog box include printing a message to the output window, running a macro, and continuing execution. You can choose any combination of these options. The first, printing a message, allows you to output data about your function. There are a number of keywords you can use to output data, such as $FUNCTION for the function name and $CALLER for the name of the calling function. A full list is printed in the dialog box in Figure 9.27. You can also output your specific variable values. You do so by enclosing the variable names in curly braces.

The Continue Execution option allows you to indicate whether this is a true tracepoint or a breakpoint that contains a tracing action. If you choose to continue, you only get the trace action (message and/or macro). If you indicate not to continue, you get the trace action; plus, the debugger stops on this line of code, just like a simple breakpoint. This is essentially applying a When Hit action to a standard breakpoint.

Finally, when you select the Run a Macro option, the dialog box gives you a list of all the macros loaded in your environment for selection.

You can also combine tracepoint actions with conditions. When you do so, the action fires only when the breakpoint condition is met.

As an example, we have set a tracepoint inside the web service GetCustomerProfile (see Figure 9.27). This tracepoint prints a message to the output window when the line of code is hit and simply continues the application executing. The message we intend to print is as follows:

Function: $FUNCTION, Thread: $TID $TNAME, Customer Id {customerId}


This message will print the function name, thread ID and name (if any), and the value of the variable, customerId. Figure 9.28 shows two passes through the tracepoint output in the Output window.

Figure 9.28. The results of a tracepoint.


Viewing Data in the Debugger

After the debugger has thrown you into break mode, the next challenge is to filter all the data your application is emitting. Getting to the right data will help you find problems faster and fix them faster. Visual Studio tries to make the data available where you want it. For example, DataTips show you variable values right in the code editor. There are many similar improvements in the way Visual Studio shows debugging data in the 2005 edition. We will cover these and more throughout the following sections.

Watching Variables

A common activity in a debug session is to view the values associated with the many types in your application. There are a number of windows available to help you here. The two most obvious are the Locals and Autos windows.

Locals Window

The Locals window shows all the variables and their values for the current debug scope. This gives you a view of everything available in the current, executing method. The variables in this window are set automatically by the debugger. They are organized alphabetically in a list by name. In addition, hierarchy is also shown. For example, if a given variable relates to object type, that object's members are listed inside the variable (as a tree).

Figure 9.29 shows an example of the Locals window. In it, you can see the sample application paused inside the GetCustomerProfile method. Notice that the cust variable is expanded to show the various properties and fields associated with this object. As values are set, the results are shown in the Value column.

Figure 9.29. The Locals window.


Tip

You can edit a value in the Locals or Autos window. To do so, right-click the variable and choose Edit Value from the context menu. You can then change the value of the variable similar to using the Immediate window.


The Autos Window

Often, viewing all the locals provides too many options to sort through. This can be true when there is just too much in scope in the given process or function. To hone in on the value of the code you are looking at, you can use the Autos window. This window shows the value of variables and expressions that are in the current executing line of code or in the prior line of code. This allows you to really focus on just the values you are currently debugging.

Figure 9.30 shows the Autos window for the same line of code as was shown in Figure 9.29. Notice the difference in what is shown. Also, notice that Visual Studio has even added specific expressions that differ from the code to the watch list. For example, the call to custDr["city"] is shown as a watch item.

Figure 9.30. The Autos window.


The Watch Windows

The Visual Studio watch windows allow you to set a custom list of variables and expressions that you want to keep an eye on. In this way, you decide the items in which you are interested. The watch windows look and behave just like the Locals and Autos windows. In addition, the items you place in watch windows persist from one debug session to another.

You access each watch window from the Debug menu or toolbar. The four watch windows are named Watch 1 through Watch 4. Having four watch windows allows you to set up four custom lists. This capability can be especially helpful if each custom list applies to a separate scope in your application.

You add a variable or expression to the watch window from either the code editor or the QuickWatch window. If you are in the code editor, you select a variable or highlight an expression, right-click, and choose the Add Watch menu item. This will take the highlighted variable or expression and place it in the watch window. You can also drag and drop the highlighted item into a watch window.

QuickWatch

The QuickWatch window is very similar to the other watch windows. However, it allows you to focus on a single variable or expression. The QuickWatch window is used less often now that DataTips exist. From the QuickWatch window, you can write expressions and add them to the watch window. When writing your expression, you have access to IntelliSense. Figure 9.31 shows the QuickWatch window.

Figure 9.31. The QuickWatch window.


The item you add to QuickWatch will be evaluated when you click the Reevaluate button. Clicking the Add Watch button will send the variable to the watch window.

Getting Data Tips

In prior versions of Visual Studio, you could highlight a section of code and view a ToolTip that indicated the value of the variable or expression. In 2005, this concept is expanded considerably with DataTips. DataTips allow you to highlight a variable or expression in the code editor and get watch information right there in the editor. This feature is more how developers work. For example, if you are looking at a line of code, you might highlight something in that line to evaluate it. Previously, this meant doing a QuickWatch. Now it simply unfolds in a DataTip.

Figure 9.32 provides an example. Here, the cursor is positioned over the cust variable that is of type Customer. Clicking on the plus sign to expand this variable unfolds the many members of the object. You can scroll through this list using the arrow at the bottom of the window. You can also right-click any member in the list and edit its value, copy it, or add it to the watch window. The magnifying glass icon next to the items in the list allows you to select a specific visualizer for a given item (more on these shortly).

Figure 9.32. The DataTips window.


You can still select an expression and have it evaluated as a DataTip. For example, if you select the portion of line 39 in Figure 9.32 that reads (bool)custDr["contact_via_email"], a DataTip will show this variable and its value of true.

Tip

The DataTips window can often get in the way of viewing code. Sometimes, you need to see the DataTips and the code underneath. In this case, holding the Control (Ctrl) key will make the DataTips window transparent for as long as you press it.


Visualizing Data

When you are looking at variable values, what you really want to get to is the data behind the object. Sometimes this data is obscured by the object model itself. For example, suppose you are looking for the data that is contained in a DataSet object. To find it, you have to dig many layers deep in a watch window or a DataTip. You have to traverse the inner workings of the object model just to get at something as basic as the data contained by the object. If you've spent much time doing this in prior versions of Visual Studio, you know how frustrating it can be.

Visual Studio 2005 tries to take away this frustration and provide quick, easy access to the data contained in an object. It does so through a new tool called visualizers. Visualizers are meant to present the object's data in a meaningful way.

A few visualizers ship with Visual Studio by default. They include the following:

  • HTML Shows a browser-like dialog box with the HTML interpreted as a user might see it

  • XML Shows the XML in a structured format

  • Text Shows a string value in an easy-to-read format

  • DataSet Shows the contents of the DataSet, DataView, and DataTable objects

There is also a framework for writing and installing visualizers in Visual Studio. You can write your own and plug them into the debugger. You can also download additional visualizers and install them. The possibilities of visualizers are many: as many ways as there are to structure and view data. A few ideas might be a tree-view visualizer that displays hierarchical data or an image visualizer that shows image data structures.

You invoke a visualizer from one of the many places you view data values. This includes watch windows and DataTips. Visualizers are represented by a magnifying glass icon. Figure 9.33 shows launching a visualizer using this icon.

Figure 9.33. Launching a visualizer from the DataTip.


For a visualizer example, refer to the DataSet problem. Rather than digging through the object hierarchy to get at the data, you can now invoke the DataSet visualizer right from a DataTip. Figure 9.34 shows the visualizer in action for the customer DataSet object in the sample application.

Figure 9.34. The DataSet Visualizer.


Using the Edit and Continue Feature

Edit and Continue allows you to change code as you debug without killing your debug session. You can make a modification to a line of code or even fix a bug and keep working in break mode. Visual Basic developers who worked in versions prior to .NET should recall this powerful tool. Its absence in .NET made it one of the most requested features. The good news is that Edit and Continue was added in 2005 to both Visual Basic and C#.

There is no trick to invoking Edit and Continue. You simply make your code change during a debug session and then keep running through your code with a Step command or Continue.

The feature is turned on by default. If it is turned off, you can re-enable it using the Options dialog box available from the Tools menu.

Not all code changes you make are eligible for Edit and Continue. In fact, it should be used only in minor fixes. Any major additions to your code should not be done in debug mode just as a best practice. If your change is within the body of a method, it has a higher likelihood of passing the Edit and Continue test. Most code changes outside the method body require the debugger to restart. Some common changes that are not eligible for Edit and Continue include

  • Changing code on the current, active statement

  • Changing code on any calls on the stack that lead to the current, active statement

  • Adding new types, methods, fields, events, or properties

  • Changing a method signature

For a more exhaustive list, search MSDN for "Edit and Continue." There are similar lists for both Visual Basic and C#.

Remote Debugging

Remote debugging allows you to connect to a running application on another machine or domain and debug that application in its environment. This is often the only way to experience errors that are occurring on specific hardware. We've all heard the developer's cry, "Works on my machine." Remote debugging helps those developers figure out why their application doesn't work in other environments.

In a number of scenarios, remote debugging makes a lot of sense. They include debugging SQL serverstored procedures, web services, web applications, remote services or processes, and so on.

The hardest part about doing remote debugging is getting it set up properly. The actual debugging is no different from the debugging we've discussed thus far. However, the setup requires you to jump through a lot of hoops in terms of installation and security. These hoops are necessary because you do not, by default, want developers to easily connect debug sessions to applications on your servers.

There is some good news. Visual Studio 2005 tries to minimize and simplify the setup and configuration of remote debugging. Microsoft has introduced the Remote Debugging Monitor (msvsmon.exe) for this purpose. However, developers will still find the setup tasks somewhat arduous (but rewarding when finished). We will not cover the setup in great detail here. We suggest querying MSDN for "Remote Debugging" to get the full walkthrough.

We do offer the following, however, as a set of high-level tasks that you will need to complete to get remote debugging working:

  1. Install the remote debugging monitor (msvsmon.exe) on the remote machine being debugged. You install it using the setup application, rdbsetup.exe. You can also run it from a file share.

  2. Configure remote debugging permissions. Typically, this means giving your user account administrative access to the machine being debugged.

  3. Run the remote debugging monitor on the remote machine. This is a Windows application (with a GUI). You can also set the monitor to run as a Windows service. This capability can be useful for specific server scenarios and ASP .NET remote debugging.

  4. If your debug machine is running XP with SP2, you will have to configure the firewall for remote debugging (see MSDN documentation for details).

  5. Run Visual Studio on your debug machine as you would to debug any process. Open the project that contains the source for the process you want to debug.

  6. Attach to the running process on the remote machine using Attach to Process. You will have to browse to the machine you want to debug and find the process running on that machine.

As you can see, getting remote debugging set up can be a challenge. However, if you have a test environment that you typically debug, the setup should be a one-time operation. From there, you should be able to debug in a more realistic environment as well as walk through SQL stored procedures.




Microsoft Visual Studio 2005 Unleashed
Microsoft Visual Studio 2005 Unleashed
ISBN: 0672328194
EAN: 2147483647
Year: 2006
Pages: 195

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net