Solved

dbgheap ASSERT with MSVC 4.2

Posted on 1997-10-08
11
738 Views
Last Modified: 2013-11-20
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.
0
Comment
Question by:JohnWeidner
  • 6
  • 3
  • 2
11 Comments
 

Author Comment

by:JohnWeidner
ID: 1307735
Edited text of question
0
 
LVL 7

Expert Comment

by:faster
ID: 1307736
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
 

Author Comment

by:JohnWeidner
ID: 1307737
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
 
LVL 7

Expert Comment

by:faster
ID: 1307738
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
 

Author Comment

by:JohnWeidner
ID: 1307739
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
What Security Threats Are You Missing?

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

 
LVL 11

Accepted Solution

by:
mikeblas earned 150 total points
ID: 1307740
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
 

Author Comment

by:JohnWeidner
ID: 1307741
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
 
LVL 11

Expert Comment

by:mikeblas
ID: 1307742
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
 

Author Comment

by:JohnWeidner
ID: 1307743
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
 

Author Comment

by:JohnWeidner
ID: 1307744
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
 
LVL 11

Expert Comment

by:mikeblas
ID: 1307745
Too bad you didn't include that information in your question.

.B ekiM


0

Featured Post

Do You Know the 4 Main Threat Actor Types?

Do you know the main threat actor types? Most attackers fall into one of four categories, each with their own favored tactics, techniques, and procedures.

Join & Write a Comment

Suggested Solutions

Introduction: Displaying information on the statusbar.   Continuing from the third article about sudoku.   Open the project in visual studio. Status bar – let’s display the timestamp there.  We need to get the timestamp from the document s…
Have you tried to learn about Unicode, UTF-8, and multibyte text encoding and all the articles are just too "academic" or too technical? This article aims to make the whole topic easy for just about anyone to understand.
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
It is a freely distributed piece of software for such tasks as photo retouching, image composition and image authoring. It works on many operating systems, in many languages.

758 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

21 Experts available now in Live!

Get 1:1 Help Now