- Table of Contents
- Copyright
- About the Authors
- About the Contributors
- Acknowledgments
- Tell Us What You Think!
- Introduction
- How to Use This Book
- What You Need to Use This Book
- What's New in Visual C++ 6.0
- Contacting the Main Author
- Part I: Introduction
- Chapter 1. The Visual C++ 6.0 Environment
- Part II: MFC Programming
- Chapter 2. MFC Class Library Overview
- Chapter 3. MFC Message Handling Mechanism
- Chapter 4. The Document View Architecture
- Chapter 5. Creating and Using Dialog Boxes
- Chapter 6. Working with Device Contexts and GDI Objects
- Chapter 7. Creating and Using Property Sheets
- Chapter 8. Working with the File System
- Chapter 9. Using Serialization with File and Archive Objects
- Part III: Internet Programming with MFC
- Chapter 10. MFC and the Internet Server API (ISAPI)
- Chapter 11. The WinInet API
- Chapter 12. MFC HTML Support
- Part IV: Advanced Programming Topics
- Chapter 13. Using the Standard C++ Library
- Chapter 14. Error Detection and Exception Handling Techniques
- Chapter 15. Debugging and Profiling Strategies
- Chapter 16. Multithreading
- Chapter 17. Using Scripting and Other Tools to Automate the Visual C++ IDE
- Part V: Database Programming
- Chapter 18. Creating Custom AppWizards
- Chapter 19. Database Overview
- Chapter 20. ODBC Programming
- Chapter 21. MFC Database Classes
- Chapter 22. Using OLE DB
- Chapter 23. Programming with ADO
- Part VI: MFC Support for COM and ActiveX
- Chapter 24. Overview of COM and Active Technologies
- Chapter 25. Active Documents
- Chapter 26. Active Containers
- Chapter 27. Active Servers
- Chapter 28. ActiveX Controls
- Part VII: Using the Active Template Library
- Chapter 29. ATL Architecture
- Chapter 30. Creating COM Objects Using ATL
- Chapter 31. Creating ActiveX Controls Using ATL
- Chapter 32. Using ATL to Create MTS and COM+ Components
- Part VIII: Finishing Touches
- Chapter 33. Adding Windows Help
- Part IX: Appendix
Techniques for Debugging Your MFC Application
Make sure the debugger is positioned onscreen so that it doesn't overlap the program. Otherwise, the debugger might obscure the program being debugged.
From the TRACER application, select the Multiple Application Debugging option when debugging an application and one or more DLLs. This way, the name of the application that generated the error will appear in a prefix.
The Multiple Application Debugger option also is useful in tracking the order of events.
Remember that you can hard code breakpoints into the application by using the following statement:
DebugBreak();
Or, you can use this statement, which is more appropriate for MFC applications:
AfxDebugBreak();
Just be sure to remove these statements when building the Win32 release version.
AfxDump
AfxDump is a pointer to an object derived from the CObject class. You should call this function while in the debugger. Doing so dumps the state of an object while debugging. AfxDump should not be called directly by the program. The program should call the Dump member function of the appropriate object instead.
AfxDump is available in the debug version of MFC only.
The TRACER.EXE Utility
Included on the Tools menu of Developer Studio is the MFC Tracer application. From this window, you can enable or disable several sorts of trace messages, as well as disable tracing altogether. Note that trace output is available only while using the debugger.
You can set the following options with TRACER:
- Database Tracing
- Enable Tracing
- Internet Client Tracing
- Main Message Dispatch
- Main Message Pump
- Multiple Application Debugging
- OLE Tracing
- WM_COMMAND Dispatch
MFC Diagnostic Features
Many diagnostic features are included with the debug version of the MFC Library. All the features in the following list are included in all the classes derived from CObject. Remember that before you can use any of the MFC diagnostic features, you must enable diagnostic tracing by setting the afxTraceEnabled flag, and the afxTraceFlags must have a set level of detail. The simplest way to set these flags is to use the TRACER.EXE utility.
- Trace Output— You can trace debugging output to evaluate argument validity. See the next section, "The TRACE Macro," for more information.
- Check Program Assumptions— Use the ASSERT macro to check program assumptions. See the section "The ASSERT Macro," later in this chapter, for more information.
- ASSERT_VALIDMacro— You can perform a runtime check of an object's internal consistency. See the section "The ASSERT_VALID Macro," later in this chapter, for more information.
- Track Memory Allocations— Use the DEBUG_NEW macro to show where objects were allocated. This topic is covered later in this chapter.
- Detect Memory Leaks— You can use memory diagnostics to detect memory leaks. This topic is covered later in this chapter.
- Use Object Dumps— You can use the Dump member function to dump object contents.
The TRACE Macro
The TRACE macro is active only in the debug version of MFC. You can build a release version of the program to deactivate all TRACE calls in the program.
The TRACE macro can handle a wide range of arguments and works similarly to printf. The following block of code demonstrates the TRACE macro:
// example for TRACE int integer1 = 1; char characters[] = "two"; float float1 = 3.3; TRACE( "Integer = %d\n",integer1); TRACE( "String = %s", characters); TRACE( "Float = %f\n", float1); // The Output would be: // 'Integer = 1' // 'String = two' // 'Float = 3.3'
The TRACE macro is limited to sending up to 512 characters at one time. If this limit is exceeded, it causes an ASSERT.
Other TRACE Macros
Another group of TRACE macros is available. These macros are very useful when debugging Unicode, because the _T macro is not required.
Here are the additional TRACE macros:
| TRACE0 | Takes a format string (only) and can be used for simple text messages that are dumped to afxDump |
| TRACE1 | Takes a format string and one argument (one variable, which is dumped to afxDump) |
| TRACE2 | Takes a format string and two arguments (two variables, which are dumped to afxDump) |
| TRACE3 | Takes a format string and three arguments (three variables, which are dumped to afxDump) |
The ASSERT Macro
You use the ASSERT macro to check assumptions made by the functions in the program. The most common use of the ASSERT macro is to locate program errors during development.
The ASSERT macro catches errors only when using the debug version of MFC. It automatically is turned off and produces no code when the program is rebuilt with the release version of MFC.
This example shows how you can use the ASSERT clause:
int FunctionResult = AnyFunction(x); ASSERT (FunctionResult < 0);
If the argument expression is false (0), the program is halted and the developer is alerted. Nothing happens if the argument is true (not zero).
If the argument is false, a message box appears with the following text:
assertion failed in file <name> in line <num> Abort Retry Ignore
where <name> is the name of the source file and <num> is the line number of the assertion that failed.
Choosing Abort terminates program execution. Choosing Ignore continues the program. Choosing Retry breaks into the debugger. Neither Abort nor Ignore activates a debugger.
The ASSERT_VALID Macro
You use the ASSERT_VALID macro to test the validity of an object's internal state. ASSERT_VALID calls the AssertValid member function of the object passed as its argument.
The macro validates the pointer of the object, checks it against NULL, and calls the object's own AssertValid member functions. An alert message is displayed similar to ASSERT if any of the tests fail.
This function is available only in the debug version of MFC.
Using DEBUG_NEW to Track Memory Allocation
To assist you in keeping track of memory being allocated, the macro DEBUG_NEW is supplied with the debug version of MFC. You can use DEBUG_NEW anywhere in the code where you normally would use the new operator.
When a debug version of a program is compiled, the DEBUG_NEW macro keeps track of the filename and line number for every object it allocates. Then, when the object is dumped by DumpAllObjectsSince, each object allocated with DEBUG_NEW shows the file and line number and where it was allocated. This makes it simple to pinpoint the sources of memory leaks.
When the release version of the program is compiled, all DEBUG_NEW statements are transformed into a new operation.
First, define the macro in the source files that will have new replaced with DEBUG_NEW, as shown here:
#define new DEBUG_NEW
Now new can be used for all heap allocations. The preprocessor will substitute DEBUG_NEW when the code is compiled. In the Win32 debug version, DEBUG_NEW will create debugging information for each heap block. When the code is built with the release version, DEBUG_NEW will be replaced with a standard memory allocation (most likely, new).
Detecting Memory Leaks
Memory leaks can occur when accidentally using memory that already has been allocated, or when memory is allocated on the heap and never deallocated for reuse. Programs that run for lengths of time can really compound any of the problems mentioned earlier.
In the past, memory leaks were difficult to detect. MFC comes to the rescue by providing classes and functions you can use to detect memory leaks at the development stage. Essentially, these classes and functions take a snapshot of all memory blocks before and after a set of chosen operations. By comparing the results, it is easy to see whether all the allocated memory has been deallocated.
The set of operations can range from a single line of code to an entire program.
Using the DevStudio IDE Debugger | Next Section

Account Sign In
View your cart