Figure 1   Debug Runtime Flags

Flag Meaning
_CRTDBG_ALLOC_MEM_DF Turn on the debug heap allocations and use the memory block identifiers. This is the only flag that's on by default.
_CRTDBG_CHECK_ALWAYS_DF Check and validate all memory on each allocation and deallocation request. Setting this flag on is what catches the under and overwrites so it is very important to get it turned on.
_CRTDBG_CHECK_CRT_DF Include _CRT_BLOCK memory allocations in all leak detection and state differences.
_CRTDBG_DELAY_FREE_MEM_DF Instead of truly freeing memory, keep the block allocated and in the internal heap list. The blocks are filled with the value 0xDD so you know the memory is freed when looking at it in the debugger. By also not freeing the memory, this can help provide stress conditions for the program.
_CRTDBG_LEAK_CHECK_DF Do memory leak checking at the end of the program.


Figure 2   MemDumperValidator.h


 /*----------------------------------------------------------------------
 John Robbins
 Microsoft Systems Journal, October 1997 - Bugslayer!
 ----------------------------------------------------------------------*/
 
 #ifndef _MEMDUMPERVALIDATOR_H
 #define _MEMDUMPERVALIDATOR_H
 
 // Include the main header.
 #include "MSJDBG.h"
 
 #ifdef __cplusplus
 extern "C" {
 #endif      // __cplusplus
 
 // This library can only be used in _DEBUG builds.
 #ifdef _DEBUG
 
 ////////////////////////////////////////////////////////////////////////
 // The typedefs for the dumper and validator functions.
 ////////////////////////////////////////////////////////////////////////
 // The memory dumper function.  The only parameter is a pointer to the
 //  block of memory.  This function can output the memory data for the
 //  block any way it likes but it might be nice if it uses the same
 //  Debug CRT reporting mechanism that everything else in the runtime
 //  uses.
 typedef void (*PFNMEMDUMPER)(const void *) ;
 // The validator function.  The first parameter is the memory block to
 //  validate and the second parameter is the context information passed
 //  to the ValidateAllBlocks function.
 typedef void (*PFNMEMVALIDATOR)(const void * , const void *) ;
 
 ////////////////////////////////////////////////////////////////////////
 // Useful Macros.
 ////////////////////////////////////////////////////////////////////////
 // The macro used to set a client block value.  This is the ONLY
 //  approved means of setting a value for the dwValue field in the
 //  DVINFO structure below.
 #define CLIENT_BLOCK_VALUE(x) (_CLIENT_BLOCK|(x<<16))
 // A macro to pick out the subtype.
 #define CLIENT_BLOCK_SUBTYPE(x) ((x >> 16) & 0xFFFF)
 
 ////////////////////////////////////////////////////////////////////////
 // The header used to initialize the dumper and validator for a specific
 //  type of client block.
 ////////////////////////////////////////////////////////////////////////
 typedef struct tag_DVINFO
 {
     // The value for the client blocks.  This must be set with the
     //  CLIENT_BLOCK_VALUE macro above.  See the AddClientDV function
     //  for how to have the library assign this number.
     unsigned long   dwValue      ;
     // The pointer to the dumper function.
     PFNMEMDUMPER    pfnDump     ;
     // The pointer to the dumper function.
     PFNMEMVALIDATOR pfnValidate ;
 } DVINFO , * LPDVINFO ;
 
 /*----------------------------------------------------------------------
 FUNCTION        :   AddClientDV
 DISCUSSION      :
     Adds a client block dumper and validator to the list.  If the
 dwValue field in the DVINFO structure is ZERO, then the next value in
 the list is assigned.  This means that the value returned must always be
 passed to _malloc_dbg as the value of the client block.
     If the value is set with CLIENT_BLOCK_VALUE, then a macro can be
 used for the value to _malloc_dbg.
     No, there is no corresponding remove function.  Why possibly
 introduce bugs in debugging code?  Performance is a non issue when it
 comes to finding errors.
 PARAMETERS      :
     lpDVInfo - The pointer to the DVINFO structure.
 RETURNS         :
     1 - The client block dumper and validator was properly added.
     0 - The client block dumper and validator could not be added.
 ----------------------------------------------------------------------*/
     int AddClientDV ( LPDVINFO lpDVInfo ) ;
 
 /*----------------------------------------------------------------------
 FUNCTION        :   ValidateAllBlocks
 DISCUSSION      :
     Checks all the memory allocated out of the local heap.  Also goes
 through all client blocks and calls the special validator function for
 the different types of client blocks.
     It is probably best to call this function with the VALIDATEALLBLOCKS
 macro below.
 PARAMETERS      :
     pContext - The context information that will be passed to each
                call to the validator function.
 RETURNS         :
     None.
 ----------------------------------------------------------------------*/
     void ValidateAllBlocks ( void * pContext ) ;
 
 #ifdef __cplusplus
 ////////////////////////////////////////////////////////////////////////
 // Helper C++ class macros.
 ////////////////////////////////////////////////////////////////////////
 // Declare this macro in your class just like the MFC ones.
 #define DECLARE_MEMDEBUG(classname)                                 \
 public   :                                                          \
     static DVINFO  m_stDVInfo ;                                     \
     static void ClassDumper ( const void * pData ) ;                \
     static void ClassValidator ( const void * pData ,               \
                                  const void * pContext )       ;    \
     void * operator new ( size_t nSize )                            \
     {                                                               \
         if ( 0 == m_stDVInfo.dwValue )                              \
         {                                                           \
             m_stDVInfo.pfnDump     = classname::ClassDumper ;       \
             m_stDVInfo.pfnValidate = classname::ClassValidator ;    \
             AddClientDV ( &m_stDVInfo ) ;                           \
         }                                                           \
         return ( _malloc_dbg ( nSize              ,                 \
                                m_stDVInfo.dwValue ,                 \
                                __FILE__           ,                 \
                                __LINE__            ) ) ;            \
     }                                                               \
     void * operator new ( size_t nSize        ,                     \
                           char * lpszFileName ,                     \
                           int    nLine         )                    \
     {                                                               \
         if ( 0 == m_stDVInfo.dwValue )                              \
         {                                                           \
             m_stDVInfo.pfnDump     = classname::ClassDumper ;       \
             m_stDVInfo.pfnValidate = classname::ClassValidator ;    \
             AddClientDV ( &m_stDVInfo ) ;                           \
         }                                                           \
         return ( _malloc_dbg ( nSize              ,                 \
                                m_stDVInfo.dwValue ,                 \
                                lpszFileName       ,                 \
                                nLine               ) ) ;            \
     }                                                               \
     void operator delete ( void * pData )                           \
     {                                                               \
         _free_dbg ( pData , m_stDVInfo.dwValue ) ;                  \
     }
 
 // Declare this one at the top of the CPP file.
 #define IMPLEMENT_MEMDEBUG(classname)                               \
     DVINFO  classname::m_stDVInfo
 
 // The macro for memory debugging allocations.  If DEBUG_NEW is defined,
 // then it can be used.
 #ifdef DEBUG_NEW
 #define MEMDEBUG_NEW DEBUG_NEW
 #else
 #define MEMDEBUG_NEW new ( __FILE__ , __LINE__ )
 #endif
 
 #endif      // __cplusplus defined.
 
 ////////////////////////////////////////////////////////////////////////
 // Helper C macros.
 ////////////////////////////////////////////////////////////////////////
 
 // For C style allocations, here is the macro to use.  Unfortunately,
 //  with C it is not so easy to use the auto-increment feature of
 //  AddClientDV.
 #define INITIALIZE_MEMDEBUG(bType , pfnD , pfnV )   \
     {                                               \
         DVINFO dvInfo ;                             \
         dvInfo.dwValue = bType ;                    \
         dvInfo.pfnDump = pfnD ;                     \
         dvInfo.pfnValidate = pfnV ;                 \
         AddClientDV ( &dvInfo ) ;                   \
     }
 
 // The macros that map the C-style allocations.  It might be easier if
 //  you use macros to wrap these so you don't have to remember which
 //  client block value to drag around with each memory usage function.
 #define MEMDEBUG_MALLOC(bType , nSize)  \
             _malloc_dbg ( nSize , bType , __FILE__ , __LINE__ )
 #define MEMDEBUG_REALLOC(bType , pBlock , nSize)    \
             _realloc_dbg( pBlock , nSize , bType , __FILE__ , __LINE__ )
 #define MEMDEBUG_EXPAND(bType , pBlock , nSize )    \
             _expand_dbg( pBlock , nSize , bType , __FILE__ , __LINE__ )
 #define MEMDEBUG_FREE(bType , pBlock)   \
             _free_dbg ( pBlock , bType )
 #define MEMDEBUG_MSIZE(bType , pBlock)  \
             _msize_dbg ( pBlock , bType )
 
 // Macro to call ValidateAllBlocks
 #define VALIDATEALLBLOCKS(x)   ValidateAllBlocks ( x )
 
 #else       // _DEBUG is NOT defined
 
 #ifdef __cplusplus
 #define DECLARE_MEMDEBUG(classname)
 #define IMPLEMENT_MEMDEBUG(classname)
 #define MEMDEBUG_NEW new
 #endif      // __cplusplus
 
 #define MEMDEBUG_MALLOC(bType , nSize) malloc ( nSize )
 #define MEMDEBUG_REALLOC(bType , pBlock , nSize) \
                                        realloc ( pBlock , nSize )
 #define MEMDEBUG_EXPAND(bType , pBlock , nSize)     \
                                        _expand ( pBlock , nSize )
 #define MEMDEBUG_FREE(bType , pBlock) free ( pBlock )
 #define MEMDEBUG_MSIZE(bType , pBlock) _msize ( pBlock )
 
 #define VALIDATEALLBLOCKS(x)
 
 #endif      // _DEBUG
 
 #ifdef __cplusplus
 }
 #endif      // __cplusplus
 
 #endif      // _MEMDUMPERVALIDATOR_H

Figure 3   Dump.cpp


 /*----------------------------------------------------------------------
 John Robbins
 Microsoft Systems Journal, October 1997 - Bugslayer!
 ----------------------------------------------------------------------*/
 #include <stdio.h>
 #include <stdlib.h>
 #include <memory.h>
 #include <string.h>
 #include <iostream.h>
 #include "MemDumperValidator.h"
 
 class TestClass
 {
 public:
     TestClass ( void )
     {
         strcpy ( m_szData , "TestClass constructor data!" ) ;
     }
     ~TestClass ( void )
     {
         m_szData[ 0 ] = '\0' ;
     }
 
     // The declaration of the memory debugging stuff for C++ classes.
     DECLARE_MEMDEBUG ( TestClass ) ;
 
 private     :
     char m_szData[ 100 ] ;
 
 } ;
 
 // This set up the static DVINFO
 IMPLEMENT_MEMDEBUG ( TestClass ) ;
 
 // The functions that you must implement to do the dumping and
 //  validating.
 #ifdef _DEBUG
 void TestClass::ClassDumper ( const void * pData )
 {
     TestClass * pClass = (TestClass*)pData ;
     _RPT1 ( _CRT_WARN ,
             " TestClass::ClassDumper : %s\n" ,
             pClass->m_szData ) ;
 }
 void TestClass::ClassValidator ( const void * pData   ,
                                  const void *          )
 {
     // Do any validation on the data here,
     TestClass * pClass = (TestClass*)pData ;
     _RPT1 ( _CRT_WARN                           ,
             " TestClass::ClassValidator : %s\n" ,
             pClass->m_szData                     ) ;
 }
 #endif
 
 typedef struct tag_SimpleStruct
 {
     char szName[ 256 ] ;
     char szRank[ 256 ] ;
 } SimpleStruct ;
 
 // The dumper and validator for simple string data memory.
 void DumperOne ( const void * pData )
 {
     _RPT1 ( _CRT_WARN , " Data is : %s\n" , pData ) ;
 }
 
 void ValidatorOne ( const void * pData , const void * pContext )
 {
     // Do any validation on the string data here.
     _RPT2 ( _CRT_WARN ,
             " Validator called with : %s : 0x%08X\n" ,
             pData , pContext ) ;
 }
 
 // The dumper and validator for the structure allocations.
 void DumperTwo ( const void * pData )
 {
     _RPT2 ( _CRT_WARN                       ,
              " Data is Name : %s\n"
              "         Rank : %s\n"         ,
              ((SimpleStruct*)pData)->szName   ,
              ((SimpleStruct*)pData)->szRank    ) ;
 }
 
 void ValidatorTwo ( const void * pData , const void * pContext )
 {
     // Do any structure validations here.
     _RPT2 ( _CRT_WARN ,
             " Validator called with : %s : 0x%08X\n" ,
             pData , pContext ) ;
 }
 
 // The C functions, unfortunately, need to have predefined values for
 //  the client blocks.  The best bet is to try and make them high
 //  enough that they stay out of the way.  In a real world application,
 //  these should all be centrally located in a single header file that
 //  everyone includes.
 #define ONE_CB  CLIENT_BLOCK_VALUE(10)
 #define TWO_CB  CLIENT_BLOCK_VALUE(11)
 
 void main ( void )
 {
     cout << "At start of main\n" ;
 
     // Do the memory debugging initialization stuff for type one.
     INITIALIZE_MEMDEBUG ( ONE_CB , DumperOne , ValidatorOne )  ;
     // The memory debugging initialization for number two.
     INITIALIZE_MEMDEBUG ( TWO_CB , DumperTwo , ValidatorTwo )  ;
 
     // Allocate the class with the memory debugging new.
     TestClass * pstClass ;
     //pstClass = MEMDEBUG_NEW TestClass ;
     pstClass = new TestClass ;
 
     // Allocate the two C types.
     char * p = (char*)MEMDEBUG_MALLOC ( ONE_CB , 10 ) ;
     strcpy ( p , "VC VC" ) ;
 
     SimpleStruct * pSt =
             (SimpleStruct*)MEMDEBUG_MALLOC ( TWO_CB ,
                                              sizeof ( SimpleStruct ) ) ;
 
     strcpy ( pSt->szName , "Pam" ) ;
     strcpy ( pSt->szRank , "CINC" ) ;
 
     // Validate all the blocks in the list.
     VALIDATEALLBLOCKS ( NULL ) ;
 
     cout << "At end of main\n" ;
 
     // Everything will get dumped as part of the memory leak checking.
 
 }

Figure 4   MemDumperValidator.cpp


 /*----------------------------------------------------------------------
 John Robbins
 Microsoft Systems Journal
 October 1997 - Bugslayer!
 ----------------------------------------------------------------------*/
 
 #include "PCH.h"
 #include "MemDumperValidator.h"
 #include "CRTDBG_Internals.h"
 
 // An internal typedef to just keep the source lines from being 600
 //  characters long.
 typedef int (*PFNCOMPARE) ( const void * , const void * ) ;
 
 ////////////////////////////////////////////////////////////////////////
 // File static variables.
 ////////////////////////////////////////////////////////////////////////
 
 // The variable that is checked to see if life is initialized.
 static BOOL             g_bLibInit = FALSE          ;
 
 // The actual pointer to the array of client block dumpers and
 //  initializers.  This is allocated out of a private heap.
 static LPDVINFO         g_pstDVInfo = NULL          ;
 
 // The number of items in the g_pstDVInfo array.
 static DWORD            g_dwDVCount = 0             ;
 
 // The highest client subblock value used.  This is so we can add items
 //  out of range of the highest seen so far.
 static DWORD            g_dwMaxSubtype = 0          ;
 
 // The private heap that this library will allocate out all data from.
 static HANDLE           g_hHeap     = NULL          ;
 
 // The critical section that everything will be protected with.
 static CRITICAL_SECTION g_stCritSec                 ;
 
 // The pointer to the previous dump client function.  If this library
 //  does not have a dumper for the client block
 static _CRT_DUMP_CLIENT g_pfnPrevDumpClient = NULL  ;
 
 ////////////////////////////////////////////////////////////////////////
 // Internal file prototypes.
 ////////////////////////////////////////////////////////////////////////
 
 // The library initializer.
 static BOOL InitializeLibrary ( void ) ;
 
 // The library shutdown function.
 static BOOL ShutdownLibrary ( void ) ;
 
 // The dump function that the actual RTL will call.
 static void DumpFunction ( void * pData , size_t nSize ) ;
 
 // The function that the RTL will call when doing all client blocks.
 static void DoForAllFunction ( void * pData , void * pContext ) ;
 
 // Compares DVINFO structures for qsort and bsearch.
 static int CompareDVInfos ( LPDVINFO pDV1 , LPDVINFO pDV2 ) ;
 
 // Finds the registered dumper-validator block for a particular piece of
 //  memory.
 static LPDVINFO FindRegisteredBlockType ( void * pData ) ;
 
 // Keeps the maximum subtype straight.
 static BOOL CheckMaxSubType ( ) ;
 
 /*//////////////////////////////////////////////////////////////////////
 
 The AutoMatic class.
 
     This class is simply one that is used to create a static variable,
 g_cBeforeAndAfter below.  Since all constructors are called before
 main/WinMain and destructors are called after main/WinMain, it gives me
 a chance to set up and tear down the library.
     Overall, there are some hacks in here you should be aware of.  The
 first is that the initialization segment for the whole file is declared
 as compiler.  This is not recommended at all.  However, there is no
 other way of ensuring static initialization order other than how things
 are ordered on the command line.  Since this can be rather difficult
 when libraries like MFC use #pragma comment to automagically link
 themselves in, this seems to be the only way.
     The second big hack is that the destructor does the memory leak
 checking and then shuts it off.  This is because the RTL clears out the
 dump client before calling _CrtDumpMemoryLeaks on shutdown.  While I
 suspect that the reasoning for clearing out the dump function is good,
 it makes it rather difficult to use the nice little architecture I set
 up here.  My guess is that since the RTL is shutting down, and the
 user's dump function could be using the RTL, it could lead to crashes.
 I don't use any CRTL calls in here except to call the debug reporting
 scheme.  Since _CrtDumpMemoryLeaks uses these calls, they are safe to be
 called.
     Finally, in the constructor, I force all the debugging flags on
 because what's the sense of having a debug build if you are not going to
 take advantage of everything at your disposal?
 //////////////////////////////////////////////////////////////////////*/
 
 #pragma warning (disable : 4074)
 #pragma init_seg(compiler)
 class AutoMatic
 {
 public      :
     AutoMatic ( )
     {
         int iFlags = _CrtSetDbgFlag ( _CRTDBG_REPORT_FLAG ) ;
         iFlags |= _CRTDBG_CHECK_ALWAYS_DF ;
         iFlags |= _CRTDBG_DELAY_FREE_MEM_DF ;
         iFlags |= _CRTDBG_LEAK_CHECK_DF ;
         _CrtSetDbgFlag ( iFlags ) ;
     }
     ~AutoMatic ( )
     {
         if ( TRUE == g_bLibInit )
         {
             EnterCriticalSection ( &g_stCritSec ) ;
 
             // Do the leak checking, then turn off the option so the
             //  CRT does not show it.
             _CrtDumpMemoryLeaks ( ) ;
             int iFlags = _CrtSetDbgFlag ( _CRTDBG_REPORT_FLAG ) ;
             iFlags &= ~_CRTDBG_LEAK_CHECK_DF ;
             _CrtSetDbgFlag ( iFlags ) ;
             ShutdownLibrary ( ) ;
         }
     }
 } ;
 
 // The static class.
 static AutoMatic g_cBeforeAndAfter ;
 
 ////////////////////////////////////////////////////////////////////////
 // Public Implementation starts here.
 ////////////////////////////////////////////////////////////////////////
 
 int AddClientDV ( LPDVINFO lpDVInfo )
 {
     BOOL bRet = TRUE ;
 
     __try
     {
         __try
         {
             ASSERT ( NULL != lpDVInfo ) ;
             ASSERT ( FALSE ==
                           IsBadCodePtr ( (FARPROC)lpDVInfo->pfnDump) ) ;
             ASSERT ( FALSE ==
                        IsBadCodePtr ( (FARPROC)lpDVInfo->pfnValidate ));
 
             if ( ( NULL == lpDVInfo )                                ||
                  ( TRUE ==
                       IsBadCodePtr((FARPROC)lpDVInfo->pfnDump     ) )||
                  ( TRUE ==
                       IsBadCodePtr((FARPROC)lpDVInfo->pfnValidate ) )  )
             {
                 _RPT0 ( _CRT_WARN , "Bad parameters to AddClientDV\n" );
                 return ( FALSE ) ;
             }
 
             // Has everything been initialized?
             if ( FALSE == g_bLibInit )
             {
                 InitializeLibrary ( ) ;
             }
 
             // Block access to the library.
             EnterCriticalSection ( &g_stCritSec ) ;
 
             // Do the trivial case add first.
             if ( 0 == g_dwDVCount )
             {
                 ASSERT ( NULL == g_pstDVInfo ) ;
 
                 g_dwDVCount = 1 ;
 
                 // Check if we are supposed to make up the new client
                 //  value.
                 if ( 0 == CLIENT_BLOCK_SUBTYPE ( lpDVInfo->dwValue ) )
                 {
                     lpDVInfo->dwValue =
                         CLIENT_BLOCK_VALUE ( g_dwDVCount ) ;
                     g_dwMaxSubtype = 1 ;
                 }
                 else
                 {
                     g_dwMaxSubtype =
                         CLIENT_BLOCK_SUBTYPE ( lpDVInfo->dwValue ) ;
                     if ( FALSE == CheckMaxSubType ( ) )
                     {
                         g_dwDVCount = 0 ;
                         __leave ;
                     }
                 }
                 g_pstDVInfo =
                        (LPDVINFO)HeapAlloc ( g_hHeap                  ,
                                              HEAP_GENERATE_EXCEPTIONS |
                                               HEAP_ZERO_MEMORY        ,
                                              sizeof ( DVINFO )        );
                 g_pstDVInfo[ 0 ] = *lpDVInfo ;
                 __leave ;
             }
             
             // Is this a specific value add?
             else if ( 0 != CLIENT_BLOCK_SUBTYPE ( lpDVInfo->dwValue ) )
             {
                 LPDVINFO lpDV ;
 
                 // Make sure that this value is not already in the list.
                 lpDV = (LPDVINFO)bsearch ( lpDVInfo                  ,
                                            g_pstDVInfo               ,
                                            g_dwDVCount               ,
                                            sizeof ( DVINFO )         ,
                                           (PFNCOMPARE)CompareDVInfos  );
                 ASSERT ( NULL == lpDV ) ;
 
                 if ( NULL != lpDV )
                 {
                     _RPT1 ( _CRT_WARN   ,
                             "%08X is already in the MemDumperValidate "
                             "list!\n"   ,
                             lpDVInfo->dwValue             );
                     bRet = FALSE ;
                     __leave ;
                 }
 
                 // Check the block subtype against the highest we have
                 //  seen so far.  If this is the new high, save the
                 //  value off.
                 if ( g_dwMaxSubtype <
                      CLIENT_BLOCK_SUBTYPE ( lpDVInfo->dwValue ) )
                 {
                     g_dwMaxSubtype =
                             CLIENT_BLOCK_SUBTYPE ( lpDVInfo->dwValue ) ;
                     if ( FALSE == CheckMaxSubType ( ) )
                     {
                         __leave ;
                     }
                 }
             }
 
             // Bump up the count.
             g_dwDVCount++ ;
 
             BOOL bTackOnEnd = FALSE ;
 
             // If the user wants to just get a value assigned, then
             //  the returned number is one larger than the last one in
             //  the list.
             if ( 0 == CLIENT_BLOCK_SUBTYPE ( lpDVInfo->dwValue ) )
             {
 
                 // Bump up the max subtype that we have seen.
                 if ( FALSE == CheckMaxSubType ( ) )
                 {
                     g_dwDVCount-- ;
                     __leave ;
                 }
                 bTackOnEnd = TRUE ;
                 lpDVInfo->dwValue =
                     CLIENT_BLOCK_VALUE ( g_dwMaxSubtype ) ;
             }
 
             // Reallocate the array.
             g_pstDVInfo =
                 (LPDVINFO)HeapReAlloc ( g_hHeap                     ,
                                         HEAP_GENERATE_EXCEPTIONS |
                                             HEAP_ZERO_MEMORY        ,
                                         g_pstDVInfo                 ,
                                         g_dwDVCount * sizeof ( DVINFO));
 
             if ( TRUE == bTackOnEnd )
             {
                 g_pstDVInfo[ g_dwDVCount - 1 ] = *lpDVInfo ;
             }
             else
             {
 
                 // Bummer, pound through the array and look for the slot
                 //  to insert it.
                 DWORD iCurr = 0 ;
                 DWORD dwToFind =
                           CLIENT_BLOCK_SUBTYPE( lpDVInfo->dwValue );
 
                 while ( dwToFind >
                        CLIENT_BLOCK_SUBTYPE(g_pstDVInfo[iCurr].dwValue))
                 {
                     iCurr++ ;
                     if ( iCurr == g_dwDVCount )
                     {
                         // Tack it on the end.
                         g_pstDVInfo[ g_dwDVCount - 1 ] = *lpDVInfo ;
                         __leave ;
                     }
                 }
 
                 DWORD dwDest = (DWORD)g_pstDVInfo +
                                 ( (iCurr+1) * sizeof ( DVINFO ) );
 
                 DWORD dwSrc = (DWORD)g_pstDVInfo +
                                ( iCurr * sizeof ( DVINFO ) ) ;
 
                 DWORD dwCount = ( ( g_dwDVCount - 1 ) - iCurr) *
                                 sizeof ( DVINFO ) ;
 
                 // Move memory to allow the insertion.
                 memmove ( (void*)dwDest , (void*)dwSrc , dwCount ) ;
 
                 // Insert the element.
                 g_pstDVInfo[ iCurr ] = *lpDVInfo ;
             }
         }
         __except ( EXCEPTION_EXECUTE_HANDLER )
         {
             ASSERT ( FALSE ) ;
             bRet = FALSE ;
         }
     }
     __finally
     {
         LeaveCriticalSection ( &g_stCritSec ) ;
     }
     return ( bRet ) ;
 }
 
 void ValidateAllBlocks ( void * pContext )
 {
     __try
     {
 
         // Block access to the library.
         EnterCriticalSection ( &g_stCritSec ) ;
 
         // The first thing to do is to let the CRT do its normal stuff.
         _CrtCheckMemory ( ) ;
 
         // Now, have the CRT call our library validator for all client
         //  blocks.
         _CrtDoForAllClientObjects ( DoForAllFunction ,
                                     pContext          ) ;
     }
     __finally
     {
         LeaveCriticalSection ( &g_stCritSec ) ;
     }
 }
 
 ////////////////////////////////////////////////////////////////////////
 // Private Implementation starts here.
 ////////////////////////////////////////////////////////////////////////
 
 /*----------------------------------------------------------------------
 FUNCTION        :   DumpFunction
 DISCUSSION      :
     The function the the RTL will call for all client blocks.  It
 calculates which of the user's functions will be called.
 PARAMETERS      :
 RETURNS         :
 ----------------------------------------------------------------------*/
 
 static void DumpFunction ( void * pData , size_t nSize )
 {
 
     __try
     {
         __try
         {
 
             // Block access to the library.
             EnterCriticalSection ( &g_stCritSec ) ;
 
             ASSERT ( NULL != pData ) ;
 
             LPDVINFO lpDV = FindRegisteredBlockType ( pData ) ;
 
             if ( ( NULL != lpDV ) && ( NULL != lpDV->pfnDump ) )
             {
 
                 // Call the dumper registered for this block.
                 lpDV->pfnDump ( pData ) ;
             }
 
             // This is either a normal client block (not one that the
             //  user added to this list), or does not have a registered
             //  dumper so pass it on to the previous dumper function.
             else if ( NULL != g_pfnPrevDumpClient )
             {
                 g_pfnPrevDumpClient ( pData , nSize ) ;
             }
             else
             {
 
                 // I just lifted the _printMemBlockData function out of
                 //  DBGHEAP.C and put it here.
                 _CrtMemBlockHeader * pHead ;
                 pHead = pHdr ( pData ) ;
                 ASSERT ( NULL != pHead ) ;
 
                 #define MAXPRINT 16
                 int           i                             ;
                 unsigned char ch                            ;
                 unsigned char printbuff[ MAXPRINT + 1 ]     ;
                 unsigned char valbuff[ MAXPRINT * 3 + 1 ]   ;
 
                 for ( i = 0 ;
                       i < min( (int)pHead->nDataSize , MAXPRINT ) ;
                       i++     )
                 {
                     ch = pbData(pHead)[ i ] ;
                     printbuff[ i ] = isprint( ch ) ? ch : ' ' ;
                     wsprintf ( (char*)&valbuff[ i * 3 ] , "%.2X " , ch);
                 }
                 printbuff[ i ] = '\0' ;
 
                 _RPT2 ( _CRT_WARN           ,
                          " Data: <%s> %s\n" ,
                          printbuff          ,
                          valbuff             ) ;
             }
         }
         __except ( EXCEPTION_EXECUTE_HANDLER )
         {
             ASSERT ( FALSE ) ;
             _RPT0 ( _CRT_WARN , "There was a crash in DumpFunction!\n");
         }
     }
     __finally
     {
         LeaveCriticalSection ( &g_stCritSec ) ;
     }
 }
 
 /*----------------------------------------------------------------------
 FUNCTION        :   DoForAllFunction
 DISCUSSION      :
     The function that ValidateAllBlocks will call to have run over 
 all the client blocks.  When called for a block, it will look at the
 list of registered types and if there is a validator function, then
 it will be called for the block.
 PARAMETERS      :
     pData    - The data to validate.
     pContext - The context data originally passed to ValudateAllBlocks.
 RETURNS         :
     None.
 ----------------------------------------------------------------------*/
 
 static void DoForAllFunction ( void * pData , void * pContext )
 {
     __try
     {
         __try
         {
 
             // Block access to the library.
             EnterCriticalSection ( &g_stCritSec ) ;
 
             ASSERT ( NULL != pData ) ;
 
             LPDVINFO lpDV = FindRegisteredBlockType ( pData ) ;
 
             // Only call the validator if there is one.  If there is not
             //  one, then there is nothing to do.
             if ( ( NULL != lpDV ) && ( NULL != lpDV->pfnValidate ) )
             {
 
                 // Call the validator registered for this block.
                 lpDV->pfnValidate ( pData , pContext ) ;
             }
         }
         __except ( EXCEPTION_EXECUTE_HANDLER )
         {
             ASSERT ( FALSE ) ;
             _RPT0 ( _CRT_WARN                                  ,
                     "There was a crash in DoForAllFunction!\n"  ) ;
         }
     }
     __finally
     {
         LeaveCriticalSection ( &g_stCritSec ) ;
     }
 }
 
 /*----------------------------------------------------------------------
 FUNCTION        :   InitializeLibrary
 DISCUSSION      :
     Completely initializes the library.  All of the file static
 variables are ready to be used.
 PARAMETERS      :
     None.
 RETURNS         :
     TRUE  - The library was initialized.
     FALSE - There was a problem.
 ----------------------------------------------------------------------*/
 
 static BOOL InitializeLibrary ( void )
 {
     ASSERT ( FALSE == g_bLibInit ) ;
 
     // Always start by initializing the critical section.
     InitializeCriticalSection ( &g_stCritSec ) ;
 
     // Create the private heap for this library.
     g_hHeap = HeapCreate ( HEAP_GENERATE_EXCEPTIONS , 0 , 0 ) ;
 
     // Now hook our dump function up.
     g_pfnPrevDumpClient =
             _CrtSetDumpClient ( (_CRT_DUMP_CLIENT)DumpFunction ) ;
 
     g_bLibInit = TRUE ;
 
     return ( TRUE ) ;
 }
 
 /*----------------------------------------------------------------------
 FUNCTION        :   ShutdownLibrary
 DISCUSSION      :
     Takes care of freeing all the memory and shutting down the library.
 This MUST be called after the critical section has already been grabbed
 because it will take care of releasing it and destroying it.
 PARAMETERS      :
     None.
 RETURNS         :
     TRUE  - Everything was kosher.
     FALSE - Danger, Will Robinson.
 ----------------------------------------------------------------------*/
 
 static BOOL ShutdownLibrary ( void )
 {
     ASSERT ( TRUE == g_bLibInit ) ;
 
     // Get rid of all the private memory in one fell swoop.
     BOOL bRet = HeapDestroy ( g_hHeap ) ;
     ASSERT ( TRUE == bRet ) ;
     g_hHeap = NULL ;
 
     // Set the previous dump client back.
     if ( NULL != g_pfnPrevDumpClient )
     {
         _CrtSetDumpClient ( (_CRT_DUMP_CLIENT)g_pfnPrevDumpClient ) ;
     }
 
     // Reset all of the global variables.
     g_pstDVInfo         = NULL  ;
     g_dwDVCount         = 0     ;
     g_dwMaxSubtype      = 0     ;
     g_hHeap             = NULL  ;
     g_pfnPrevDumpClient = NULL  ;
     g_bLibInit          = FALSE ;
 
     // Remember, the critical section is blocked here so clear it then
     //  get rid of it because we are done.
     LeaveCriticalSection ( &g_stCritSec ) ;
     DeleteCriticalSection ( &g_stCritSec ) ;
 
     return ( TRUE ) ;
 }
 
 /*----------------------------------------------------------------------
 FUNCTION        :   CompateDVInfos
 DISCUSSION      :
     Compares the dwValue parameters for two DVINFO structures.
 PARAMETERS      :
     pDV1, pDV2 - The structures to compare.
 RETURNS         :
     < 0 - pDV1 < pDV2
     0   - pDV1 = pDV2
     > 0 - pDV1 > pDV2
 ----------------------------------------------------------------------*/
 
 static int CompareDVInfos ( LPDVINFO pDV1 , LPDVINFO pDV2 )
 {
     ASSERT ( NULL != pDV1 ) ;
     ASSERT ( NULL != pDV2 ) ;
 
     if ( pDV1->dwValue < pDV2->dwValue )
     {
         return ( -1 ) ;
     }
     if ( pDV1->dwValue > pDV2->dwValue )
     {
         return ( 1 ) ;
     }
     return ( 0 ) ;
 }
 
 /*----------------------------------------------------------------------
 FUNCTION        :   CheckMaxSubType
 DISCUSSION      :
     A simple function that keeps the maximum subtype straight.
 PARAMETERS      :
     None.
 RETURNS         :
     TRUE  - g_dwMaxSubtype is properly updated and ready for use.
     FALSE - g_dwMaxSubtype is out of range.
 ----------------------------------------------------------------------*/
 
 static BOOL CheckMaxSubType ( )
 {
     ASSERT ( (WORD)g_dwMaxSubtype < (WORD)0xFFFF ) ;
 
     if ( g_dwMaxSubtype >= 0xFFFF )
     {
         _RPT0 ( _CRT_WARN                        ,
                 "Running max subtype value in "
                 "MemDumperValidator is too high!"  ) ;
         return ( FALSE ) ;
     }
     g_dwMaxSubtype++ ;
     return ( TRUE ) ;
 }
 
 static LPDVINFO FindRegisteredBlockType ( void * pData )
 {
 
     // Get at the header for the block to get the client type.
     _CrtMemBlockHeader * pHead ;
     pHead = pHdr ( pData ) ;
     ASSERT ( NULL != pHead ) ;
 
     DVINFO   stDVFind   ;
     LPDVINFO lpDV       ;
 
     stDVFind.dwValue = pHead->nBlockUse ;
 
     // Try and find the value.
     lpDV = (LPDVINFO)bsearch ( &stDVFind                    ,
                                g_pstDVInfo                  ,
                                g_dwDVCount                  ,
                                sizeof ( DVINFO )            ,
                                (PFNCOMPARE)CompareDVInfos    ) ;
     return ( lpDV ) ;
 }

Figure 5   CRTDBG_Internals.h


 /*----------------------------------------------------------------------
 John Robbins
 Microsoft Systems Journal, October 1997 - Bugslayer!
 ----------------------------------------------------------------------*/
 
 #ifndef _CRTDBG_INTERNALS_H
 #define _CRTDBG_INTERNALS_H
 
 #define nNoMansLandSize 4
 
 typedef struct _CrtMemBlockHeader
 {
     struct _CrtMemBlockHeader * pBlockHeaderNext        ;
     struct _CrtMemBlockHeader * pBlockHeaderPrev        ;
     char *                      szFileName              ;
     int                         nLine                   ;
     size_t                      nDataSize               ;
     int                         nBlockUse               ;
     long                        lRequest                ;
     unsigned char               gap[nNoMansLandSize]    ;
     /* followed by:
      *  unsigned char           data[nDataSize];
      *  unsigned char           anotherGap[nNoMansLandSize];
      */
 } _CrtMemBlockHeader;
 
 #define pbData(pblock) ((unsigned char *) \
                         ((_CrtMemBlockHeader *)pblock + 1))
 #define pHdr(pbData) (((_CrtMemBlockHeader *)pbData)-1)
 
 #endif      // _CRTDBG_INTERNALS_H

Figure 6   MemStressLib.cpp


 /*----------------------------------------------------------------------
 John Robbins
 Microsoft Systems Journal, October 1997 - Bugslayer!
 ----------------------------------------------------------------------*/
 
 // Get everything included.
 #include "PCH.h"
 #include "MemStressLib.h"
 #include "MemStressLibConstants.h"
 #include "CRTDBG_Internals.h"
 
 ////////////////////////////////////////////////////////////////////////
 // This library needs USER32.DLL for MessageBox so force the link
 //  against it in case the user forgets.
 ////////////////////////////////////////////////////////////////////////
 #pragma comment ( lib , "user32.lib" )
 
 ////////////////////////////////////////////////////////////////////////
 // Internal Data Structures
 ////////////////////////////////////////////////////////////////////////
 // The struct that holds a single file marked for failure and the line
 //  in the file where allocations are supposed to fail.  If the line is -1,
 //  then any allocations from the file are supposed to fail.
 // BIG NOTE: Notice that even in UNICODE builds, this structure only has
 //  character buffers. No matter what, __FILE__ is always defined in terms of
 //  chars. So everything is converted when it is read in.
 typedef struct tag_FAILFILE
 {
     char szFile[ MAX_PATH + 1 ] ;
     long lLine                  ;
 } FAILFILE , * LPFAILFILE ;
 
 // The struct that holds all of the options as they come out of the INI
 //  file.
 typedef struct tag_FAILUREINFO
 {
     // The CRT options that should be set for the user.
     BOOL        bCRTCheckMemory     ;
     BOOL        bCRTDelayMemFrees   ;
     BOOL        bCRTLeakChecking    ;
     // The general options for the hook.  If any of the UINT types are
     //  zero, then they are not active.
     BOOL        bGENFailAllAllocs   ;
     UINT        uiGENFailEveryN     ;
     UINT        uiGENFailAfterX     ;
     UINT        uiGENFailOverY      ;
     BOOL        bGENAskOnEach       ;
     // The total file count.
     UINT        uiFileCount         ;
     // The array of files to fail.
     LPFAILFILE  pFailFiles          ;
 } FAILUREINFO , * LPFAILUREINFO ;
 
 ////////////////////////////////////////////////////////////////////////
 // Unfortunately, the CRT hooks do not get told if the memory being
 //  reallocated is being done in place or will cause a free first.
 //  Thus, it is impossible to know for sure exactly how much memory is
 //  outstanding in the system.  The statistics keeps are only for calls
 //  to allocation functions (new and malloc) and deallocation functions
 //  (delete and free).
 // Memory statistics are recorded before any processing for failures
 //  takes place.
 typedef struct tag_MEMSTATS
 {
     // The total highwater mark of allocations so far.
     long lMaxAllocated      ;
     // The total number of calls to allocation functions.
     long lTotalAllocCalls   ;
     // The total number of calls to realloc functions.
     long lTotalReAllocCalls ;
     // The total number of calls to release functions.
     long lTotalReleaseCalls ;
     // The total amount of memory that is currently in use.
     long lCurrentlyInUse    ;
 } MEMSTATS , * LPMEMSTATS ;
 
 ////////////////////////////////////////////////////////////////////////
 // Static Variable Declarations
 ////////////////////////////////////////////////////////////////////////
 // The critical section that everything will be protected with.
 static CRITICAL_SECTION g_stCritSec ;
 // The one global flag that indicates that the library was properly
 //  initialized and ready for use.
 static BOOL g_bLibIsInit = FALSE ;
 // The private heap that will hold all of the allocations for this
 //  module.  Keep in mind that this library cannot do any normal CRT
 //  memory allocations because it could get into nasty recursive
 //  situations.
 static HANDLE g_hHeap = NULL ;
 // The failure information for this run of the library.
 static LPFAILUREINFO g_pFailInfo = NULL ;
 // The internal statistics.
 static MEMSTATS g_stMemStats ;
 // The previously installed hook.
 static _CRT_ALLOC_HOOK pfnPrevHook ;
 
 ////////////////////////////////////////////////////////////////////////
 // Internal Function Declarations
 ////////////////////////////////////////////////////////////////////////
 // The function that actually does the INI reading.
 static LPFAILUREINFO ProcessINIFileA ( LPCSTR szProgram ) ;
 // The actual allocation hook.
 static int AllocationHook ( int                   nAllocType ,
                             void *                pvData     ,
                             size_t                nSize      ,
                             int                   nBlockUse  ,
                             long                  lRequest   ,
                             const unsigned char * szFileName ,
                             int                   nLine       ) ;
 
 ////////////////////////////////////////////////////////////////////////
 // Function Implementation Starts Here
 ////////////////////////////////////////////////////////////////////////
 
 // The initialization function.
 int InitializeMemStressA ( const char * szProgName )
 {
     // The return value.
     BOOL bRet = TRUE ;
     __try
     {
         __try
         {
             // Check all of the basic assumptions.
             ASSERT ( FALSE == g_bLibIsInit ) ;
             ASSERT ( NULL == g_hHeap ) ;
             ASSERT ( NULL == g_pFailInfo ) ;
             ASSERT ( NULL != szProgName ) ;
 
             // Initialize the few items that all routines in here will
             //  have to share.
             InitializeCriticalSection ( &g_stCritSec ) ;
             // Now immediately, try and grab it.
             EnterCriticalSection ( &g_stCritSec ) ;
 
             memset ( &g_stMemStats , NULL , sizeof ( MEMSTATS ) ) ;
 
             // Create the private heap for this library.
             g_hHeap = HeapCreate ( HEAP_GENERATE_EXCEPTIONS , 0 , 0 ) ;
 
             // Get the failure information for the file.
             g_pFailInfo = ProcessINIFileA ( szProgName ) ;
 
             // Set the main CRT flags.
             int iFlags = _CrtSetDbgFlag ( _CRTDBG_REPORT_FLAG ) ;
             if ( TRUE == g_pFailInfo->bCRTCheckMemory )
             {
                 iFlags |= _CRTDBG_CHECK_ALWAYS_DF ;
             }
             if ( TRUE == g_pFailInfo->bCRTDelayMemFrees )
             {
                 iFlags |= _CRTDBG_DELAY_FREE_MEM_DF ;
             }
             if ( TRUE == g_pFailInfo->bCRTLeakChecking )
             {
                 iFlags |= _CRTDBG_LEAK_CHECK_DF ;
             }
             _CrtSetDbgFlag ( iFlags ) ;
 
             // Set the allocation hook.
             pfnPrevHook =
                   _CrtSetAllocHook ( (_CRT_ALLOC_HOOK)AllocationHook ) ;
 
             g_bLibIsInit = TRUE ;
 
         }
         __except ( EXCEPTION_EXECUTE_HANDLER )
         {
             ASSERT ( FALSE ) ;
             // The library was unable to initialize.
             g_bLibIsInit = FALSE ;
             bRet = FALSE ;
         }
     }
     __finally
     {
         // Make sure that we always execute this!
         LeaveCriticalSection ( &g_stCritSec ) ;
     }
     return ( bRet ) ;
 }
 
 int InitializeMemStressW ( const wchar_t * szProgName )
 {
     ASSERT ( NULL != szProgName ) ;
     char szBuff[ MAX_PATH + 1 ] ;
     int  iRet ;
 
     iRet = WideCharToMultiByte ( CP_ACP             ,
                                  0                  ,
                                  szProgName         ,
                                  -1                 ,
                                  szBuff             ,
                                  sizeof ( szBuff )  ,
                                  NULL               ,
                                  NULL                ) ;
     ASSERT ( 0 != iRet ) ;
     return ( InitializeMemStressA ( szBuff ) ) ;
 }
 
 int TerminateMemStress ( void )
 {
     // The return value.
     BOOL bRet = TRUE ;
 
     __try
     {
         __try
         {
             ASSERT ( TRUE == g_bLibIsInit ) ;
             ASSERT ( NULL != g_pFailInfo ) ;
 
             // Now immediately, try and grab it.
             EnterCriticalSection ( &g_stCritSec ) ;
 
             // Notice that I do not explicitly free any of the memory
             //  allocated out of the private heap.  Since the whole
             //  block gets destroyed in a single call, there is no
             //  reason to do each subblock.
             BOOL bHRet = HeapDestroy ( g_hHeap ) ;
             ASSERT ( FALSE != bHRet ) ;
             g_hHeap = NULL ;
             g_pFailInfo = NULL ;
             g_bLibIsInit = FALSE ;
 
             // Set the allocation hook back to what it was.
             _CrtSetAllocHook ( (_CRT_ALLOC_HOOK)pfnPrevHook ) ;
 
         }
         __except ( EXCEPTION_EXECUTE_HANDLER )
         {
             ASSERT ( FALSE ) ;
             bRet = FALSE ;
         }
     }
     __finally
     {
         LeaveCriticalSection ( &g_stCritSec ) ;
         // Just get rid of the critical section.
         DeleteCriticalSection ( &g_stCritSec ) ;
     }
     return ( bRet ) ;
 }
 
 /*----------------------------------------------------------------------
 Function        :   AllocationHook
 Discussion      :
     The memory allocation hook that is installed to handle the memory
 failures.
 Parameters      :
     nAllocType - The type of allocation operation, _HOOK_ALLOC,
                  _HOOK_REALLOC, or _HOOK_FREE.
     pvData     - The pointer to the data if nAllocType is _HOOK_FREE,
                  otherwise NULL.
     nSize      - The size of the memory block.
     nBlockUse  - The type of memory block, _CRT_BLOCK, etc.
     lRequest   - The block request number.
     szFileName - The file that had the memory operation.
     nLine      - The line where the memory operation took place.
 Returns         :
     TRUE  - The allocation function should succeed.
     FALSE - The allocation function should fail.
 ----------------------------------------------------------------------*/
 static int AllocationHook ( int                   nAllocType    ,
                             void *                pvData        ,
                             size_t                nSize         ,
                             int                   nBlockUse     ,
                             long                  /*lRequest*/  ,
                             const unsigned char * szFileName    ,
                             int                   nLine          )
 {
     BOOL bRet = TRUE ;
 
     __try
     {
         __try
         {
             // Grab the critical section.
             EnterCriticalSection ( &g_stCritSec ) ;
 
             // The CRT blocks must be ignored or we will cause many
             //  problems.
             if ( _CRT_BLOCK == nBlockUse )
             {
                 __leave ;
             }
 
             // Skip all the processing for _HOOK_FREE type calls.
             if ( _HOOK_FREE == nAllocType )
             {
                 _leave ;
             }
 
             // Now go through the litany of possible failure cases.
             if ( TRUE == g_pFailInfo->bGENFailAllAllocs )
             {
                 bRet = FALSE ;
                 __leave ;
             }
             if ( TRUE == g_pFailInfo->bGENAskOnEach )
             {
                 char szBuff [ 1024 ] ;
                 wsprintfA ( szBuff       ,
                             k_MSGFMT     ,
                             szFileName   ,
                             nLine         ) ;
 
                 if ( IDYES == MessageBoxA ( GetActiveWindow ( ) ,
                                             szBuff              ,
                                             k_MSGTITLE          ,
                                             MB_YESNO          ) )
                 {
                     bRet = FALSE ;
                     __leave ;
                 }
             }
             if ( 0 != g_pFailInfo->uiGENFailEveryN )
             {
                 if ( 0 == g_stMemStats.lTotalAllocCalls %
                           g_pFailInfo->uiGENFailEveryN    )
                 {
                     bRet = FALSE ;
                     __leave ;
                 }
             }
             if ( 0 != g_pFailInfo->uiGENFailAfterX )
             {
                 if ( g_pFailInfo->uiGENFailAfterX     <=
                      (UINT)g_stMemStats.lMaxAllocated    )
                 {
                     bRet = FALSE ;
                     _leave ;
                 }
             }
             if ( 0 != g_pFailInfo->uiGENFailOverY )
             {
                 if ( nSize > g_pFailInfo->uiGENFailOverY )
                 {
                     bRet = FALSE ;
                     _leave ;
                 }
             }
             if ( 0 != g_pFailInfo->uiFileCount )
             {
                 // Because the value for the __FILE__ macro seems to be
                 //  randomly generated, I need to strip off any path
                 //  stuff that may, or may not, be there.
                 char szJustFilename[ MAX_PATH ] ;
                 char szExt[ MAX_PATH ] ;
 
                 _splitpath ( (LPCSTR)szFileName   ,
                               NULL                ,
                               NULL                ,
                               szJustFilename      ,
                               szExt                ) ;
 
                 strcat ( szJustFilename , szExt ) ;
 
                 for ( UINT i = 0                   ;
                       i < g_pFailInfo->uiFileCount ;
                       i++                           )
                 {
                     if ( 0 ==
                         stricmp ( g_pFailInfo->pFailFiles[i].szFile ,
                                   (LPCSTR)szJustFilename             ) )
                     {
                         if (( -1 == g_pFailInfo->pFailFiles[i].lLine )||
                            ( nLine ==
                                     g_pFailInfo->pFailFiles[i].lLine  ))
                         {
                             bRet = FALSE ;
                             __leave ;
                         }
                     }
                 }
             }
         }
         __except ( EXCEPTION_EXECUTE_HANDLER )
         {
         }
     }
     __finally
     {
         // Now that the allocation success or failure is known, do the
         //  statistics upkeep.
         if ( _HOOK_FREE == nAllocType )
         {
             // Since the size is always zero when this is
             //  called for a free function, get the
             //  information ourselves.
             _CrtMemBlockHeader * pHead = pHdr(pvData) ;
 
             g_stMemStats.lTotalReleaseCalls++ ;
             g_stMemStats.lCurrentlyInUse -= pHead->nDataSize ;
             // Since the user can easily start and stop the hook
             //  whenever they want to, we might see extra calls to frees
             //  here.  If that is the case, don't let the currently
             //  used go to less than zero.
             if ( g_stMemStats.lCurrentlyInUse <= 0 )
             {
                 g_stMemStats.lCurrentlyInUse = 0 ;
             }
         }
         else if ( TRUE == bRet )
         {
             g_stMemStats.lMaxAllocated += nSize ;
             g_stMemStats.lCurrentlyInUse += nSize ;
             switch ( nAllocType )
             {
                 case _HOOK_ALLOC    :
                     g_stMemStats.lTotalAllocCalls++ ;
                     break ;
                 case _HOOK_REALLOC  :
                     g_stMemStats.lTotalReAllocCalls++ ;
                     break ;
                 default     :
                     ASSERT ( FALSE ) ;
                     break ;
             }
         }
 
         // Make sure that we always execute this!
         LeaveCriticalSection ( &g_stCritSec ) ;
     }
     return ( bRet ) ;
 }
 
 
 // The function that does the INI reading.
 static LPFAILUREINFO ProcessINIFileA ( LPCSTR szProgram  )
 
 {
     // Allocate the space for the buffer that will be returned.
     LPFAILUREINFO pFailInfo =
                (LPFAILUREINFO)HeapAlloc ( g_hHeap                    ,
                                           HEAP_GENERATE_EXCEPTIONS |
                                             HEAP_ZERO_MEMORY         ,
                                           sizeof ( FAILUREINFO )      );
 
     // Look to see if the section with the program name really exists.
     //  If it does not the default section will be used.
     char    szBuff [ MAX_PATH + 1 ] ;
     DWORD   dwRet                   ;
     LPCSTR  lpAppName               ;
 
     dwRet = GetPrivateProfileSectionA ( szProgram         ,
                                         szBuff            ,
                                         sizeof ( szBuff ) ,
                                         k_INI_FILENAME     ) ;
     if ( 0 == dwRet )
     {
         // The section for this program does not exist so use the
         //  default.
         lpAppName = k_INI_DEFAULTSECTION ;
     }
     else
     {
         lpAppName = szProgram ;
     }
 
     // Pound through and get all of the fields.
     pFailInfo->bCRTCheckMemory =
                            GetPrivateProfileIntA ( lpAppName         ,
                                                    k_INI_CRTCHECKMEM ,
                                                    1                 ,
                                                    k_INI_FILENAME     );
     pFailInfo->bCRTDelayMemFrees =
                            GetPrivateProfileIntA ( lpAppName         ,
                                                    k_INI_CRTDELAYMEM ,
                                                    1                 ,
                                                    k_INI_FILENAME     );
     pFailInfo->bCRTLeakChecking =
                            GetPrivateProfileIntA ( lpAppName           ,
                                                    k_INI_CRTCHECKLEAKS ,
                                                    1                   ,
                                                    k_INI_FILENAME     );
 
     pFailInfo->bGENFailAllAllocs =
                          GetPrivateProfileIntA ( lpAppName             ,
                                                  k_INI_GENFAILALLALLOCS,
                                                  0                     ,
                                                  k_INI_FILENAME       );
     pFailInfo->uiGENFailEveryN =
                       GetPrivateProfileIntA ( lpAppName                ,
                                              k_INI_GENFAILEVERYNALLOCS ,
                                              0                         ,
                                              k_INI_FILENAME           );
     pFailInfo->uiGENFailAfterX =
                        GetPrivateProfileIntA ( lpAppName               ,
                                               k_INI_GENFAILAFTERXBYTES ,
                                               0                        ,
                                               k_INI_FILENAME          );
     pFailInfo->uiGENFailOverY =
                         GetPrivateProfileIntA ( lpAppName              ,
                                                k_INI_GENFAILOVERYBYTES ,
                                                0                       ,
                                                k_INI_FILENAME         );
     pFailInfo->bGENAskOnEach =
                         GetPrivateProfileIntA ( lpAppName              ,
                                                k_INI_GENASKONEACHALLOC ,
                                                0                       ,
                                                k_INI_FILENAME         );
 
     // Now for the fun stuff, loop through and load the files to fail.
     pFailInfo->uiFileCount =
                        GetPrivateProfileIntA ( lpAppName               ,
                                               k_INI_GENNUMFILEFAILURES ,
                                               0                        ,
                                               k_INI_FILENAME          );
     if ( 0 != pFailInfo->uiFileCount )
     {
         // Allocate the space in the structure.
         pFailInfo->pFailFiles =
                     (LPFAILFILE)HeapAlloc ( g_hHeap                    ,
                                           HEAP_GENERATE_EXCEPTIONS |
                                               HEAP_ZERO_MEMORY         ,
                                           sizeof ( FAILFILE ) *
                                                pFailInfo->uiFileCount );
         char   szCurrFile [ 30 ] ;
         LPSTR  pEndOfConst       ;
         UINT   uiCurrIndex       ;
         UINT   uiCount           ;
         LPSTR  pTok1             ;
         LPSTR  pTok2             ;
         long   lData             ;
 
         strcpy ( szCurrFile , k_INI_GENFILEFAILPREFIX ) ;
         pEndOfConst = szCurrFile + strlen ( k_INI_GENFILEFAILPREFIX ) ;
 
         uiCurrIndex = 0 ;
 
         for ( uiCount = 1                       ;
               uiCount <= pFailInfo->uiFileCount ;
               uiCount++                          )
         {
             // Build up the key to read.
             itoa ( uiCount , pEndOfConst , 10 ) ;
 
             // Read the key.
             dwRet = GetPrivateProfileStringA ( lpAppName         ,
                                                szCurrFile        ,
                                                ""                ,
                                               szBuff             ,
                                               sizeof ( szBuff )  ,
                                               k_INI_FILENAME      ) ;
 
             // If nothing was read, then just go for the next one.
             if ( 0 == dwRet )
             {
                 continue ;
             }
 
             // Extract the two parts of the string.
             pTok1 = strtok ( szBuff , k_SEP_FILEANDLINE ) ;
             pTok2 = strtok ( NULL , k_SEP_FILEANDLINE ) ;
 
             if ( ( NULL == pTok1 ) || ( NULL == pTok2 ) )
             {
                 continue ;
             }
 
             // Convert the second string into a number.
             lData = atol ( pTok2 ) ;
 
             if ( 0 == lData )
             {
                 continue ;
             }
 
             // Whew!  We can go ahead and add this one.
             pFailInfo->pFailFiles[ uiCurrIndex ].lLine = lData ;
             strcpy ( pFailInfo->pFailFiles[uiCurrIndex].szFile ,
                      pTok1                                      ) ;
 
             // Update the array counter.
             uiCurrIndex++ ;
         }
 
         // Set the count to the proper count since some entries might
         //  have been thrown away.
         pFailInfo->uiFileCount = uiCurrIndex ;
     }
     return ( pFailInfo ) ;
 }