Lesson 4: Using the Integrated Debugger

Armed with the general overview of debugging provided in Lesson 3, you are now ready to use the Visual C++ debugger. As you will see, the debugger greatly facilitates the debugging process and can help you find nearly any bug you are likely to encounter in Windows software development.

After this lesson, you will be able to:

  • Use the Visual C++ integrated debugger and understand the extensive information presented in the debugger windows.
  • Monitor a running program and set breakpoints to interrupt the program.
  • Single-step through a program.
  • Use the debugger's Edit and Continue feature to correct code on the fly while debugging.
  • Debug COM-based programs and components.
  • Use the Test Container utility to monitor ActiveX controls.
Estimated lesson time: 40 minutes

Breakpoints

The debugger does not interrupt the program being debugged. Rather, the program interrupts itself when it locates a marker set in the text editor. The marker is called a breakpoint. As the program executes, the debugger sleeps. It regains control when the executing program triggers a breakpoint.

The debugger recognizes two different types of breakpoints, one based on location in the code, and the other based on program data. A location breakpoint is a marker attached to a particular instruction in your source code, similar to using a bookmark in the Visual C++ text editor. A data breakpoint depends on data instead of code. Use a data breakpoint to suspend execution of your program when the value of a variable changes. A data breakpoint can be useful if you suspect a variable is being incorrectly altered in your program, but you aren't sure in which location. The data breakpoint triggers the debugger to interrupt execution when the variable changes or becomes a certain value (for example, when a pointer is reassigned or when the variable x exceeds a value of 500).

Setting Breakpoints

The text editor is the best place to begin debugging. Location breakpoints are by far the most commonly used breakpoints, and are easy to set in the Visual C++ debugger. You simply need to have a general idea of where you think your program is going wrong.

  • To set a location breakpoint
    1. Open the program's source files and locate the line where you want to interupt the execution of the program.
    2. Click anywhere on the line to place the insertion point there, then press F9 to set a location breakpoint. The editor marks the line by placing a small red dot in the selection margin to the left of the line.
    3. To remove a location breakpoint, press F9 again to toggle the breakpoint off.
    4. You can also set or remove a location breakpoint by right-clicking the line. On the shortcut menu that appears (shown in Figure 13.4), select the Insert/Remove Breakpoint command to set or clear a breakpoint.

    Figure 13.4 The Insert/Remove Breakpoint command

    Though less convenient, you can also set a location breakpoint through the Breakpoints dialog box. This dialog box provides the only means for setting data breakpoints and two other variations, called conditional breakpoints and message breakpoints. Conditional breakpoints trigger as soon as a variable is set to a specified value. Message breakpoints trigger as soon as a specified message is received by a window.

    Breakpoints Dialog Box

    To invoke the Breakpoints dialog box shown in Figure 13.5, press CTRL+B or click the Breakpoints command on the Edit menu. The three tabs in the dialog box let you set location, data, conditional, and message breakpoints.

    Figure 13.5 The Breakpoints dialog box

    Location Breakpoints

    The Breakpoints dialog box provides several enhancements for location breakpoints that often prove useful. For instance, you can type the name of a function in the Break At text box to set a location breakpoint at the first line of the function, or type the name of a label to set a breakpoint at the labeled line. The text in the Break At control is case-sensitive, so it must match the function name or label exactly. A C++ function name must include the class name and scope resolution operator. Thus, the entry OnInitDialog() does not specify a valid breakpoint location, but CDemoDlg::OnInitDialog() does.

    Data Breakpoints

    The Breakpoints dialog box provides the only means for setting a data break-point. On the Data tab in the Breakpoints dialog box, enter the name of the variable or the expression you want the debugger to monitor. Type an ex-pression in the form of a standard C/C++ conditional expression, such as the following:

    i == 100 or nCount > 25

    The debugger can monitor a range of variables identified by a pointer, such as an array or structure name, provided you dereference the pointer in the expression.

  • To set a data breakpoint for an array or structure
    1. In the Breakpoints dialog box, click the Data tab.
    2. For an array, type the array name followed by [0] to dereference the pointer—iArray[0], for example.
    3. To monitor more than just the first element of the array, set the number of elements to monitor in the text box labeled Enter the number of elements in an array or structure. Notice that this is the number of elements, not the number of bytes.
    4. To set a data breakpoint for a structure, precede the pointer variable with the asterisk dereference operator, as in pStruct.

    Similarly, to monitor a string of character bytes that the variable pString points to, type *pString in the Enter The Expression text box. In the Enter the number of elements text box, type the number of bytes that you want the debugger to monitor. Typing pString without the asterisk dereference operator will result in the breakpoint being triggered only if pString is changed to point somewhere else. In this case, the debugger monitors pString itself, not the contents of the string to which it points.

    Your program's execution speed can slow significantly if you set more than four data breakpoints, or if any of the data breakpoints are set on a variable residing on the stack.

    Conditional Breakpoints

    The debugger responds to a conditional breakpoint only if a specified condition is TRUE when control reaches the marked instruction. Each time an instruction marked as a conditional breakpoint executes, the debugger evaluates the expression and suspends program flow only if the expression does not evaluate to zero.

  • To set a conditional breakpoint
    1. In the Breakpoints dialog box, click the Location tab.
    2. Specify the source code instruction at which to set the breakpoint.
    3. Click the Condition button (shown in Figure 13.5) to display the Breakpoint Condition dialog box.
    4. In the Enter the expression to be evaluated text box, type the breakpoint condition in the form of a C/C++ conditional expression.

    Message Breakpoints

    A message breakpoint attaches to a window procedure. Execution breaks when the window procedure receives a specified message, such as WM_SIZE or WM_COMMAND. Message breakpoints are not always useful in C++ programs that use MFC, since window procedures usually lie buried inside the MFC framework. To trap a specific message in an MFC program, set a location breakpoint for the function that handles the message, which is identified in the class's message map.

    Running the Debugger

    Once you have created a debug build and have established where and under what conditions you want your program to stop, you are ready to execute your program.

  • To start the debugger
    1. On the Build menu, click Start Debug. You will be presented with four choices: Go, Step Into, Run to Cursor, and Attach to Process.
    2. When you have set at least one breakpoint in the source code, click Go. The debugger runs the program, suspending execution when the flow of execution in your program reaches a location breakpoint or triggers a data breakpoint.
    3. Click Step Into to start program execution, and stop at the first command.
    4. Click the Run to Cursor command to run the program and break at the source line containing the insertion point. If no source file is open in the text editor, the Run to Cursor command is unavailable. Otherwise, it allows you to quickly jump into a program without setting a breakpoint.
    5. Click the Attach to Process command to launch the debugger and attach it to a program that is currently executing.

    The debugger provides shortcut keys for the first three subcommands of Start Debug, so you don't have to pull down the Build menu to begin debugging. The shortcut keys are F5 for Go, F11 for Step Into, and CTRL+F10 for Run to Cursor.

    Debugger Windows

    When the program you are debugging stops at a breakpoint, the debugger updates its windows with information about the program's current state. The most important debugger window is the source window, which shows the source code where the program stopped. A small yellow arrow, the instruction pointer, appears in the selection margin to the left of the interrupted instruction. The instruction pointer identifies the instruction that has not yet executed, but is next in line to do so when the program resumes execution.

    When control is returned to the debugger, the Debug toolbar appears on the screen. The six buttons identified in Figure 13.6 as debugger windows act as toggles that expose or hide dockable windows containing information about the current state of the program. Table 13.4 describes the type of information displayed in the debugger windows.

    click to view at full size.

    Figure 13.6 Debugger windows buttons

    Table 13.4 Information Displayed by the Debugger Windows Buttons

    Window Information displayed
    Watch Current values of variables and expressions tracked by the debugger
    Variables Current values of variables accessed at or near the break location
    Registers Current contents of the CPU registers
    Memory Memory contents at a specified address
    Call Stack List of called functions that have not yet returned
    Disassembly Assembly language translation of the compiled code that supplements the source window on the screen

    Variables Window and Watch Window

    The Variables window displays information about variables relevant to the location where the program flow has been suspended. Variables referenced by the instruction that last executed, and usually one or two previous instructions, appear in the Variables window. You can change the value of a variable by double-clicking it in the Variables window and typing a new value.

    The Watch window shows the current values of specified variables no matter where they are referenced in the program. To add a variable to the Watch window, double-click the dotted new-entry box in the window and type the variable name. The QuickWatch tool shown in Figure 13.6 lets you query for a current value without adding the variable to the Watch window. If the variable name appears on the screen, the debugger offers a more convenient way to query for its current value. Simply position the mouse pointer over the variable name in the Source window to see a ToolTip showing the current value.

    Memory Window and Registers Window

    The Memory window shows the contents of memory at a given address. The Memory window is useful for examining buffers that might not appear in the Variables window. Determine the value of the buffer pointer by locating it in the Variables or Watch window, then type or paste the address into the text box in the Memory window and press ENTER.

    The Registers window shows the state of the processor's registers as they existed when the program was suspended. The Registers window is generally used only when the Disassembly window is active, showing the code in assembly language.

    Call Stack Window

    The Call Stack window shows the route the program has taken to reach the point you are examining. It answers the question, "How did I get here?"

    A call stack is a list of nested functions, each of which have been called and none of which have yet returned. The list begins with the current function that contains the point of interruption, and continues in reverse order toward the oldest parent function. MFC programs often wind through many nested functions hidden in the framework, so the call stack for these programs can be lengthy.

    Stepping Through Code

    The Debug toolbar holds a group of four buttons (shown in Figure 13.7) that allow you to step through a suspended program. You can recognize the Step tools by the arrows and curly braces on them. In the order shown, the buttons activate the Step Into, Step Over, Step Out, and Run To Cursor commands. We've already discussed the Run To Cursor command; this section covers the Step Into, Step Over, and Step Out commands.

    click to view at full size.

    Figure 13.7 The Debug toolbar Step tools

    The Step Into and Step Over commands (or their respective shortcut keys F11 and F10) let you single-step through the program. When you select Step Into or Step Over, the debugger allows the program to resume execution, but for only one instruction. After the instruction finishes, the debugger again suspends execution. If the Disassembly view is available, the Step tools act on individual assembly instructions instead of the high-level C/C++ instructions. If applied to an instruction that calls a function, Step Over halts at the instruction following the call, whereas Step Into halts at the first instruction of the function called. For instructions that do not call a function, Step Into and Step Over have the same effect.

    The Step Out command is useful for leaving a function. The command executes the rest of the current function, then stops at the next statement following the function call. In other words, when applied to a function call, the Step Into and Step Out commands together have the same effect as Step Over.

    Edit and Continue

    Through its Edit and Continue feature, Visual C++ lets you permanently fix many problems directly in the debugger's source window, without having to exit the debugger and recompile. When you continue running the program after editing the source, Visual C++ first compiles the revised code and replaces the affected module with the corrected version.

    Edit and Continue has certain limitations; this feature does not recognize source changes that are impossible, impractical, or unsafe to compile while debugging, such as:

    • Alterations to exception handler blocks.
    • Wholesale deletions of functions.
    • Changes to class and function definitions.
    • Changes to static functions.
    • Changes to resource data in the project's resource (.rc) file.

    Attempting to resume execution through Edit and Continue after making any of these changes causes the debugger to display an error message in the status bar that explains the problem. You have the option of continuing to debug using the original code, or closing the debugger and recompiling the revised code normally.

    Debugging COM Components

    The Visual C++ debugger easily handles in-process COM components such as ActiveX controls, though such components require a container application to run them. If you have written the component's container as another project, it does not matter where you begin debugging, whether in the container's project or in the component's project. The debugger crosses the boundary between projects transparently as execution flow moves from client to server and back again.

    To begin debugging in the component's project, first specify the container application in which you want to embed the component. On the Debug tab of the Project Settings dialog box, type the container's path and file name in the text box labeled Executable For Debug Session. You can also browse for the container by clicking the arrow next to the text box. This action displays a small menu of choices, one of which is ActiveX Control Test Container.

    ActiveX Control Test Container

    The Test Container utility shown in Figure 13.8 is a general-purpose container utility for ActiveX controls. Visual C++ provides the Test Container so that you can debug and test ActiveX controls without having to write a corresponding container for them.

    click to view at full size.

    Figure 13.8 The ActiveX Control Test Container

    The first step is to set breakpoints in your project's source files. If you have chosen the Test Container as the host for your component during debugging, the debugger launches the utility automatically, as it would any other container. When a breakpoint in the method triggers, focus returns to the Visual C++ debugger, ready for you to single-step through your source code.

    You can also execute the Test Container without the debugger, as the next practice exercise demonstrates.

    Executing ActiveX Controls in the Test Container

    In this practice exercise, you will launch the Test Container utility manually, without starting the debugger. You will learn how to open an ActiveX control in the Test Container and program it through its methods and properties.

  • To start the Test Container
    1. On the Tools menu, click ActiveX Control Test Container.
    2. When the Test Container appears, click New Control on the toolbar, which displays a list of registered ActiveX controls.
    3. If you have built your own ActiveX control and it has been properly registered, locate it in the list. Otherwise, double-click any control in the list to activate it, such as the Calendar control that comes with Internet Explorer.
    4. Once the control appears in the Test Container window, click the Invoke Methods tool to display the Invoke Methods dialog box.
    5. Click the Method Name box to expose a list of methods that the control exports. If you have activated the Calendar control or a similar object, select the BackColor (PropPut) property method in the list. Methods identified as PropPut write a control property; PropGet methods read a property.
    6. Enter the new property value in the Parameter Value box. To set a color, enter the decimal equivalent of an RGB value, such as 255 for bright red.
    7. Click the Invoke button to call the method with the new value. If the property affects the control's appearance (as does the BackColor property), you should see the control change accordingly.
    8. Press DELETE to delete the selected control, then close the Test Container.

    Debugging Out-of-Process COM Servers

    Many out-of-process COM servers can run as stand-alone programs, and thus do not technically require an executing container. However, as a server's methods and events execute only when activated by a client, you should specify a path to the client instead of the server on the Debug tab of the Project Settings dialog box. Set breakpoints in the server's source code as you want them, then launch the debugger to automatically start the client application. When both the client and server are active, switch to the client and invoke the commands necessary to activate the server's functions that contain the breakpoints. As with an in-process server, the Visual C++ debugger becomes active when execution reaches a breakpoint. You can then examine the server's code.

    Lesson Summary

    This lesson presented the Visual C++ debugger, an invaluable tool for locating the causes of errors in a program. You learned how to set both location and data breakpoints through the Breakpoints dialog box, how to launch the debugger, and how to single-step through code when the debugged program halts at a breakpoint. The lesson also described the debugger's Edit and Continue feature, which allows you to correct source code without having to exit the debugger and recompile.

    A typical debugging session begins in the text editor. You can set breakpoints in the project source code, at those sections of your program that you want to interrupt, before you start the debugger. The debugger in turn automatically starts your program and gains control when a breakpoint is triggered. The debugger displays six dockable windows:

    • Watch displays the values of selected data.
    • Variables displays information pertaining to the current instruction.
    • Call Stack contains a list of nested function calls.
    • Registers displays the current contents of the CPU registers.
    • Memory displays any area of accessible memory.
    • Disassembly translates source code into equivalent assembly instructions.

    In-process ActiveX controls are DLLs and therefore are debugged with the same ease as normal DLLs. Visual C++ also provides the Test Container utility, which serves as a general-purpose container application for running ActiveX controls. The Test Container enables you to debug and test your ActiveX control without having to develop a separate container for it.



    Microsoft Press - Desktop Applications with Microsoft Visual C++ 6. 0. MCSD Training Kit
    Desktop Applications with Microsoft Visual C++ 6.0 MCSD Training Kit
    ISBN: 0735607958
    EAN: 2147483647
    Year: 1999
    Pages: 95

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