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.
JohnWeidnerAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

JohnWeidnerAuthor Commented:
Edited text of question
0
fasterCommented:
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.
0
JohnWeidnerAuthor Commented:
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?
0
Upgrade your Question Security!

Your question, your audience. Choose who sees your identity—and your question—with question security.

fasterCommented:
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.
0
JohnWeidnerAuthor Commented:
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.
0
mikeblasCommented:
The ASSERT means exactly what the comment says: you're calling the wrong delete operator for the given block of memory. It's very likely, though not certain, that the destructor for the object in question isn't being called.

It's possible that you have a bad cast in your application. For example, this code will cause the assertion that you reference to fire:

   // implicit cast is dangerous
   void* pObject = new CWnd;

   // calling the wrong delete operator because of bad cast!
   delete pStringObject;
   // this would have worked
   // delete ((CWnd*) pObject);

The assertion means that the runtimes think you're calling operator delete for objects which don't have destructors on memory that was actually initialized by a constructor.  Or, that you were calling operator delete for objects which have destructors on memory that was acutally not initialized by a constructor.

Of course, your program seems to work in release mode because there are no ASSERTs in a release build: you're just lying to yourself. That's like saying you've noticed that you don't need to get gasoline for your car when you ride your bicycle to work.

If you look at the stack trace after the assert fires, you'll be led to the incorrect call which triggered the assertion and you can fix it.

The problem appears in 4.2 because 4.1 didn't do this check.

It's _possible_, but far less likely, that this problem is being caused by simple corruption; it's possible that you've overwritten the nUse field in the memory preamble to the block you're trying to delete, and that's botching the check that the libraries are trying to do for you.  But it's not nearly as likely as improperly using datatypes or using an incorrect explicit or implicit cast.

If you want, post the code that's causing the problem and I'll explain exactly what's wrong with it

.B ekiM


0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
JohnWeidnerAuthor Commented:
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 );
            }
      }
}

0
mikeblasCommented:
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

0
JohnWeidnerAuthor Commented:
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()
0
JohnWeidnerAuthor Commented:
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".)
0
mikeblasCommented:
Too bad you didn't include that information in your question.

.B ekiM


0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
System Programming

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.