The Microsoft Foundation Class (MFC) Library and Visual C++ help you debug your applications in various ways. This section presents a few useful general debugging techniques, followed by more detailed debugging topics.
This section includes the following topics:
Microsoft Visual C++ 5.0 introduces debug support for the C run-time library. The new debug version of the library supplies many diagnostic services that make debugging programs easier. This section describes some of the debugging routines and macros used for diagnostic purposes.
This section includes the following topics:
Visual C++ adds extensive debugging support to the C run-time library, enabling you to step directly into run-time functions when debugging an application. The library also provides a variety of tools to keep track of heap allocations, locate memory leaks, and find other memory-related problems.
Much of the heap-checking technology included in the debug version of the C run-time library has been moved from the Microsoft Foundation Class Library. To use the heap-checking technology, you must now link debug builds of MFC applications with a debug version of the run-time library.
The library contains debug reporting functions, including:
® _CrtDbgReport and _CrtIsValidPointer; macros for verification and reporting
® _ASSERT and _RPTn; functions that use a debug heap
® Debug versions of malloc, free, calloc, realloc, new and delete
® Heap-monitoring functions, such as _CrtCheckMemory and _CrtDumpMemoryLeaks
The library also includes functions, such as _CrtSetDumpClient and _CrtSetAllocHook, which enable you to write and install your own hook functions with special features you need when debugging a complex application.
To use these routines, you must define the_DEBUG flag. These routines do nothing in a retail build of an application. The C run-time debug functions are available for Windows 95 and Windows NT. For more information about how to use the new debug routines, see "Using C Run-Time Library Debugging Support" in the Visual C++ online documentation.
The run-time debugging routines are only active when you run an application built with debug information included. Unlike error-checking, assertions do not decrease execution speed, because the code "disappears" in release builds. This section describes the ASSERT routine, the VERIFY routine, the CObject::AssertValid function, and the ASSERT_VALID macro.
ASSERT Routine
The ASSERT routine is used to ensure a specific assumption. If the assertion is false, the macro displays an assertion message box containing the source file name and line number. The user is given the choice of terminating or debugging the program. This macro is commonly used to validate function arguments and return values.
CWnd* pWnd = GetParent();
ASSERT(pWnd != NULL);
VERIFY Routine
The VERIFY routine evaluates the condition in the Debug and Release environments, but prints and terminates (if appropriate) only in the Debug environment. VERIFY is very similar to ASSERT when you are in Debug mode. In Release mode, however, the contained expression is executed — but not verified. VERIFY is useful for wrapping function calls that return a pointer.
CObject::AssertValid Function
This debug function determines whether the associated object is internally valid. All MFC Library classes override this function to provide for internal consistency checking. When you create a reusable class, you should override CObject::AssertValid.
ASSERT_VALID Macro
MFC uses the ASSERT_VALID macro to force a call to an object's AssertValid function. Typically, whenever a function is expecting a valid CObject or CObject pointer as a parameter, the function should use the ASSERT_VALID macro to validate the object. As with the ASSERT macro, ASSERT_VALID is called only in a Debug build, as shown in the following sample code:
CShapesDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
When you run an application under the integrated debugger, you may find debugger-enhancing routines useful. The information that these functions provide is displayed in the output window of the debugger. This topic briefly describes the TRACE macro and the CObject::Dump function.
TRACE Macro
The TRACE macro provides a way to place formatted output strings into the debug stream; it is similar to the C run-time printf statement. For example,
TRACE("The number of rectangles is now %d", nRect);
CObject::Dump Function
The Dump function causes the internal state of an associated object to be displayed in an output window.
Under Debug mode, the MFC framework automatically calls the Dump function for any CObject objects not properly destroyed on application termination. Therefore, when you write your own class, you should override the Dump function for the base class to provide diagnostic services for your derived class. The overridden Dump usually calls the Dump function of its base class before printing data members unique to the derived class. CObject::Dump prints the class name if your class uses the IMPLEMENT_DYNAMIC or IMPLEMENT_SERIAL macros.
When you call Dump for an object, you must supply a single argument of type CDumpContext. The global object afxDump is provided for this purpose.
For more information about the MFC diagnostic services, see "Diagnostics" in the online MFC Encyclopedia, and the MFC Technical Note TN7: "Debugging Trace Options" in the Visual C++ online documentation.Using Tracer
To help debug Windows-based programs, MFC provides a Tracer program. Tracer.exe is a small MFC Programming Utilities sample program that displays, in a debugging output window or console window, messages about the internal operation of the MFC Library, as well as warnings and errors if something goes wrong in your application. You can view as much or as little debugging information as you want.
Tracer will often warn you about problems, and provide more detailed explanations of errors.
Using Tracer
Tracer enables you to set the options in Afx.ini, and is installed in your Bin directory by Visual C++ Setup. A sample Afx.ini file is provided in the Mfc\Src subdirectory. This .ini file turns on diagnostic messages and uses the standard options. You should place this Afx.ini file in your Windows directory, or run Tracer.exe to create a new Afx.ini file and set options. Changes to Afx.ini will take effect in any debug MFC application launched after the changes are saved.
The global integer afxTraceFlags is used to turn on the built-in reporting features of MFC and to store all flags. Global integer afxTraceFlags uses each bit to select a trace reporting option. You can turn any bit on or off, thereby altering the generated report information. The numeric values associated with each option are provided in the Afxwin.h header file.
To run Tracer, click MFC Tracer on the Tools menu. The MFC Trace Options dialog box will appear, providing eight trace options. The first option, Enable tracing, turns tracing on or off. The other seven options enable you to select which types of trace messages will be sent to the debugging window or console.
For more information about Tracer options, see Technical Note 7: "Debugging Trace Options" in the Visual C++ online documentation.
u To turn on Tracer output
1. Compile your program with the _DEBUG symbol defined, and link with a debug version of the MFC Library. Debugging and trace options are available only in the debug version of the Library.
2. Enable the afxTraceEnabled flag. You can do this in several ways; using Tracer to do so is recommended.
3. Customize afxTraceFlags to determine the level of detail you want in Tracer messages.
Where the Output Goes
When afxTraceEnabled is true, then Tracer output (and default afxDump output) will go to the output window if present. When afxTraceEnabled is false, Tracer output and afxDump output will not be displayed.
If a debugger is present, Tracer output will go to the debugger's output window. If no debugger is present, you will not be able to see Tracer output.
Spy++ (Spyxx.exe) is a Win32-based utility that gives you a graphical view of the system's processes, threads, windows, and messages.
Spy++ has a toolbar and hyperlinks to help you work faster. It also provides a Refresh command to update the active view, a Window Finder Tool to make spying easier, and a Font dialog box to customize view windows. Additionally, Spy++ saves and restores user preferences.
Similar to Spy++, the PView process viewer (PView.exe) enables you to examine and modify many characteristics of the processes and threads running on your system.
To see a demonstration showing how to use the Spy++ utility to view the system's processes, threads, windows, and messages, click this icon.
For more information about the Spy++ or PView process viewer utilities, see "Spy++ Home Page" in the Visual C++/Windows Utilities online documentation.
Self-Check Questions
1. After preparing a debug version of your project, why is it necessary to rebuild the project for release?
A. When a project is compiled for a Debug build, the _DEBUG preprocessor is defined. In a Release build, the
_NDEBUG preprocessor is defined.
B. The Debug version uses a different set of DLLs than the Release version.
C. The default for the Release version of a project is optimized for speed and performance.
D. All of the above.
2. Which one of the following statements is false when using the Developer Studio debugger to set and manage breakpoints?
A. You can disable an individual breakpoint or you can disable all breakpoints in an application.
B. The breakpoints you set cannot be saved as part of your project.
C. To set a breakpoint on a source statement extending across two or more lines, you must set the breakpoint
on the last line of the statement.
D. An asterisk (*) in the Breakpoint check box indicates that the breakpoint is not supported on the current
platform.
3. What kind of information do Visual C++ browse information files provide?
A. Information about the symbols (classes, functions, data, macros, and types) in a program.
B. The relationships between all the files in a project.
C. Debugging messages for ActiveX components.
D. All of the above.
4. Which one of the following statements applies to the MFC VERIFY macro?
A. Used to validate function arguments that return a value rather than a pointer.
B. VERIFY is very similar to ASSERT when you are in Release mode.
C. Evaluates a condition or expression in Debug and Release mode.
D. Prints an error message and terminates (if appropriate) in Debug and Release mode.
Lab 5.1: Using Debugger
In this lab, you will fix errors, build, and run an MFC application using the Developer Studio debugger.
To see a demonstration that shows what you will accomplish during the lab, click this icon.
Estimated time to complete this lab: 35 minutes
To complete the exercises in this lab, you must have the required software. For detailed information about the labs and setup for the labs, see Labs in this course.
Objectives
After completing this lab, you will be able to:
® Build and run debug versions of a sample MFC application.
® Use debugging techniques to fix application errors.
® Use the Tracer program to monitor the values of a variable in the application.
® Use the Step Into debugging feature to step through the functions in a sample application.
Prerequisites
There are no prerequisites for this lab.
Exercises
The following exercises provide practice with the concepts and techniques covered in this chapter.
® Exercise 1: Fixing Application Errors
In this exercise, you will use the Microsoft Developer Studio debugger to find and fix a printing problem in a sample application.
® Exercise 2: Stepping Through an Application
In this exercise, you will use the Step Into debugging feature to explore a sample application function by function.
- Exercise 1: Fixing Application Errors
In this exercise, you will use the debugging techniques to correct a printing problem in a sample application, TextReader. The TextReader application is similar to the Reader application discussed in Chapter 4: Analyzing a Document/View Application.
You can find the code that forms the basis for this exercise in \Labs\Ch05\Lab01\Baseline. Copy these files to your working directory.
u Build the application in Debug mode
1. In Developer Studio, open the TextReader project.
2. On the Build menu, click Set Active Configuration. Select the Debug version of the TextReader application, and then click OK.
3. Build the TextReader application.
u Run the application
1. In Developer Studio, on the Build menu, point to Start Debug, and then click Go.
2. On the File menu, click Open, and then open any text file. View the file on the screen.
3. On the File menu, click Print Preview. Notice that the lines are chopped in the print preview.
4. Close the application.
u Debug the application
1. Switch back to Developer Studio.
2. In the ClassView pane, double-click the member function CTextReaderView::OnDraw to open the code associated with it.
3. Add the following lines of code before the For loop to correct the chopped lines problem:
int nYDelta = 0;TEXTMETRIC tm;
pDC->GetTextMetrics(&tm);
nYDelta = tm.tmHeight;
The TEXTMETRIC structure contains basic information about a physical font. And GetTextMetrics retrieves the metrics for the current font.
4. Insert a TRACE statement inside the For loop to enable tracing:
TRACE( "nYDelta = %d\n",nYDelta );
5. On the Tools menu, click MFC Tracer. Select the Enable tracing check box, and then click OK. This will start the Tracer application.
6. Build and run the application in Debug mode.
7. Open a text file in your TextReader application.
8. From the View menu, click Output. Monitor the value of nYDelta in the output window located at the bottom of the screen. This variable indicates the height of the character.
9. On the File menu, click Print Preview.
10. Monitor the value of nYDelta. Notice the change in the nYDelta value.
11. Exit the TextReader application and the Tracer application.
12. Open the CTextReaderView::OnDraw member function. Change the nYPos statement from the following:
nYPos +=15;
to:
nYPos +=nYDelta;
u Build and run the TextReader application
— Rebuild and run the project. When printing the text or viewing the text on the screen, the output should be correct; in other words, the lines should not be chopped.
You can find the completed code for this exercise in \Labs\Ch05\Lab01\Ex01.
- Exercise 2: Stepping Through an Application
In this optional exercise, you will use the debugger to step through the application created in Lab 4.1: Hand-Coding a Minimal MFC Application. You can explore WinMain and the built-in MFC functions supplied by the framework using the debugger.
You can find the code that forms the basis for this exercise in \Labs\Ch04\Lab01\Ex01. Copy these files to your working directory.
u Explore MFC function using the integrated debugger
1. On the Build menu, point to Start Debug, and then click Step Into (or press F11).
2. The debugger will start and place you in the WinMain function of the implementation file, AppModul.cpp, as follows:
extern "C" int WINAPI _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance, LPTSTR lpCmdLine,
int nCmdShow)
{
// call shared/exported WinMain
return AfxWinMain(hInstance, hPrevInstance,
lpCmdLine, nCmdShow); }
3. Press F11 two more times and you will be exploring AfxWinMain. As you step through the code, you will find all the expected calls.
No comments:
Post a Comment