Link to home
Start Free TrialLog in
Avatar of JohnWeidner
JohnWeidner

asked on

dbgheap ASSERT with MSVC 4.2

I've recently started using the 4.2 compiler (with the vc42b.exe patch) only a large project that previously worked fine when built with the 4.1 compiler.  Now, when built with the 4.2 compiler, the debug version of my program triggers an ASSERT in dbgheap in the _free_dbg function on line 1051.  The code is:

        /* Error if freeing incorrect memory type */
        _ASSERTE(pHead->nBlockUse == nBlockUse);

Does anyone know what's causing this and how to fix it.  The program seems to run fine if I just ignore the ASSERT.
Avatar of JohnWeidner
JohnWeidner

ASKER

Edited text of question
I have had some similar (not identical) porblem before.  There must be some bugs relating with memory handling and the heap is corrupted.  Although sometimes it seems to be working fine under some situations, the bug is there.  One thing you can check is whether you allocate and free a block of memory in the same place (an exe or dll), if not, you need to use dll version of c runtime library.  Another method is to put _heapchk() in your code to see where exactly cause the problem.
The VC 4.2 documentation for _dbgheap says it will only work under NT;  I am using Windows 95.   The problems seems to be that I am using "new" to allocate a CObject derived object.  When I use "new" it properly calls the CObject "new" operator.   But when I use "delete" it does not call the CObject "delete" operator.  Do you know how to force the compiler to call the CObject version of delete?  Do you know why it doesn't call it?
There is no reason why it does not call the base class's destructor.  How do you know it is not called and it is the cause of your problem?  When do you delete the object?  Is it in the same exe or dll where you new it?

_heapchk() is available on 95.  Besides, when you have some problem in the heap, the behaviour of your program can be very strange.
I didn't say that the destructors weren't being called.  It's the wronge "delete operator" that's being called.  And yes, the object is being allocated (/w new) and deallocated (/w delete) from the same source file.

Let's see if someone else has more experience with this specific problem.
ASKER CERTIFIED SOLUTION
Avatar of mikeblas
mikeblas

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Mike,

Here's some parts of the code.  The code of interest is the calls to "new" and "delete" for the DataValueRef object.

---header file----

class DataValueRef : public CObject {
      GString                  value ;            // current value
      int                  flags;            // modified,...
      ReferenceObjectList      references ;      // list of screen objects that use this value
      int                  listIndex ;
public:
      DataValueRef()
      {
            references = NULL ;
            flags = 0;
            listIndex = 0;
      }
      virtual ~DataValueRef();
      void      TraceDump( void );
        .
        .
        .
}


---- from .cpp file-----
       .
       .
       .

DataValueRef * DataSynchronize::AddValueReference( DataLoc * dataLoc, GString * newValue,
                                    ScriptValueObj *refObj )
{
      char                  dataLocStr[40];
      DataValueRef *            valueRefPtr ;

      if ( dataLoc->IsEmpty() ) {
            return NULL ;
      }

      // create string to use for the CMapStringToOb
      DataLocToString( dataLocStr, dataLoc, KEY_DELIMITER );

      if ( 0 == map.Lookup( dataLocStr, (CObject*&) valueRefPtr ) ) {
            // not found in map so we must create a new valueRef item

            valueRefPtr = new DataValueRef ;

            // and add it to the list
            map.SetAt( dataLocStr, valueRefPtr );

            //set flag that indicates that we really do need to get
            //the current data from the server.
            resyncNeeded++;
      }


      if ( newValue != NULL ) {
            valueRefPtr->SetValue( *newValue );
      }


      if ( refObj != NULL ) {
            valueRefPtr->AddReference( refObj );
      }
      

      return valueRefPtr ;
}

        .
        .
        .

void DataSynchronize::ClearFlag( int flagValue )
{
      POSITION            pos ;
      GString                  key ;
      DataValueRef *            valueRefPtr ;

      // Iterate through the entire map

      pos = map.GetStartPosition();
      while ( pos != NULL ) {
            map.GetNextAssoc( pos, key, (CObject*&)valueRefPtr );
            
            if ( flagValue == DV_MODIFIED_BY_CLIENT &&
                 valueRefPtr->HasReferences() == FALSE ) {

                 // No need for this item to be on the list
                 map.RemoveKey( key );
                   delete  valueRefPtr ;
            }
            else {
                  // clear the flag for each item
                  valueRefPtr->ClearFlag( flagValue );
            }
      }
}

I think you're improperly removing the key from your map.

I can't tell, because I don't know what a GString is (well, I don't know what _this_ GString is) and I don't know how your ReferenceObjectList class is declared. You'd also need to show a stack trace leading to the assertion. The problem is either in your map class declaration or in the destructor for one of the collected objects.

Nowhere in your question did you mention using collections.

.B ekiM

I finally had a chance to look into this some more.   The problem occurs in other places that have nothing to do with the map collections.  To be sure, I tried doing a new and then immediately a delete of my DataValueRef object.  It still caused the assert.   Then I decided that I'd isolate this class from my big program and place it in a small test program to see what happened.   In the small program there were NO ASSERTs.  I figured it might be a compiler option or something, but the options were the same.  To be sure it wasn't some compiler problem, I copied the .obj file and re-linked.  Same thing - the small program works and my big program ASSERTS.  Okay, I'm thinking that maybe I'm corrupting memory earlier in my large program - so I decided to do the test first thing in the apps constructor - but it still asserts.  My guess now is that the problem must be with the linking or loading of the program.  How does a C++ program determine which delete operator to call?

Here's the call stack when it asserts:

_free_dbg_lk(void * 0x01d51ad0, int 1) line 1051 + 51 bytes
_free_dbg(void * 0x01d51ad0, int 1) line 970 + 13 bytes
operator delete(void * 0x01d51ad0) line 282 + 12 bytes
??3CObject@@SGXPAX@Z + 10 bytes
DataValueRef::`scalar deleting destructor'() + 39 bytes
TestMemAssert() line 21 + 35 bytes
CGeoApp::CGeoApp() line 76
$E333() line 142 + 16 bytes
$E336() + 11 bytes
_initterm(void (void)* * 0x0053e2bc, void (void)* * 0x0053e434) line 511
WinMainCRTStartup() line 274 + 15 bytes
KERNEL32! bff88f75()
KERNEL32! bff88e23()
KERNEL32! bff8783f()
00000005()

Here's a stack trace of the small program that doesn't assert:

_free_dbg_lk(void * 0x00770bc0, int 4) line 1051
_free_dbg(void * 0x00770bc0, int 4) line 970 + 13 bytes
CObject::operator delete(void * 0x00770bc0) line 44 + 12 bytes
DataValueRef::`scalar deleting destructor'() + 39 bytes
TestMemAssert() line 21 + 35 bytes
CMfc42App::CMfc42App() line 44
$E250() line 49 + 16 bytes
$E253() + 11 bytes
_initterm(void (void)* * 0x0040b208, void (void)* * 0x0040b314) line 511
WinMainCRTStartup() line 274 + 15 bytes
KERNEL32! bff88f75()
KERNEL32! bff88e23()
KERNEL32! bff8783f()
00000004()
The problem turned out to be caused by linking with some libraries built with the debug versions of the MFC libraries and some built with the non-debug version.   Once I made sure that all the libraries were built with the same options the ASSERTs went away.  (You can tell what version your libraries are built with by using dumpbin /all library_name and then looking at the part starting with "RAW DATA".)
Too bad you didn't include that information in your question.

.B ekiM