Title: CPP9305: Temporary Object Memory Leak in Foundation Classes
Document Number: Q98867 Publ Date: 30-JUN-1993
Product Name: Microsoft C++ Foundation Classes
Product Version: 2.00
Operating System: MS-DOS
----------------------------------------------------------------------
The information in this article applies to:
- Microsoft Foundation Classes for Windows, version 2.0
----------------------------------------------------------------------
Summary:
PROBLEM ID: CPP9305001
SYMPTOMS
Under some circumstances, an application created with Microsoft
Foundation Classes version 2.0 allocates temporary objects on the
application heap and does not delete them. While the memory
diagnostics in the debugging version of the Foundation Classes
libraries do not detect this memory leak, the error is apparent
when Heap Walker is used to monitor the heap.
CAUSE
A HANDLE is attached to both a permanent object and a temporary
object. When the HANDLE is detached from the permanent object, it
is also detached from the temporary object. Consequently, the
temporary object is never destroyed.
RESOLUTION
Do not remove the HANDLE from the temporary map when it is
detached. To do so, remove one line of code from the WINHAND.CPP
source file. In Visual C++, remove line 158 which reads as follows:
m_temporaryMap.RemoveKey((MAPTYPE)h);
It is also necessary to change the arguments to some ASSERT macros
to prevent detaching a temporary object from causing an assertion
failure. It is not necessary to Detach a temporary object because
it generally serves no useful purpose. However, because Microsoft
Foundation Class Libraries version 1.0 support this practice,
version 2.0 supports it as well. In WINHAND.CPP, change lines 145
and 173 from
ASSERT(ph[0] == h);
to:
ASSERT(ph[0] == h || ph[0] == NULL);
After making these changes, rebuild the Microsoft Foundation Class
Libraries using the MAKEFILE in the library source directory (by
default, C:\MSVC\MFC\SRC). The MAKEFILE supports building all
variations of the Foundation Class Libraries. For more information
on using the MAKEFILE, please refer to the comments at the
beginning of the MAKEFILE.
STATUS
Microsoft has confirmed this to be a problem in the Microsoft
Foundation Class Libraries version 2.0 for Windows. We are
researching this problem and will post more information here as it
becomes available.
More Information:
The Foundation Class Libraries maintain two maps for each HANDLE used
in the Microsoft Windows environment. The Libraries maintain four map
pairs, for HDCs, HGDIOBJECTs, HMENUs, and HWNDs. For each handle type,
the Libraries maintain two separate maps: a temporary map and a
permanent map. When the Library maps a HANDLE to a C++ object, it
checks the permanent map first and then checks the temporary map.
Under some circumstances, a HANDLE appears in both maps. A serious
memory leak occurs when a HANDLE is Detached (or unmapped) from its
permanent C++ object. This removes the HANDLE from both maps even
though the temporary object is not destroyed. Because the temporary
C++ object is not present in the temporary map when the
CWinApp::OnIdle() function attempts to delete all temporary objects,
the temporary C++ object remains in memory. For example, the following
code example causes a memory leak that involves one temporary CDC
object:
CDC* pDC = CDC::FromHandle(hDC); // creates temporary object
CDC dc;
dc.Attach(hDC); // now in permanent map as well
dc.Detach(hDC); // removed from temp and permanent map
// pDC is never deleted
Even though the code sequence above is rare, it occurs quite often
processing objects in the CFrameWnd class. CFrameWnd processes a
WM_ERASEBKGND message in an OnEraseBkgnd handler. Windows sends
WM_ERASEBKGND before it sends the WM_PAINT message. The Library
creates a temporary object in the OnEraseBkgnd handler. When the
application receives the WM_PAINT message, it creates a permanent
CPaintDC object on the stack. This creates a situation similar to that
above except that the temporary and permanent objects result from
processing different messages.
Because CWinApp::OnIdle() is not called between the WM_ERASEBKGND
message and WM_PAINT messages, the temporary object created processing
the WM_ERASEBKGND message is not removed.
This problem does not generally affect applications created in
AppWizard because CFrameWnd objects in an application that uses CView
child windows do not receive WM_PAINT messages under normal
circumstances.
Additional reference words: 2.00
COPYRIGHT Microsoft Corporation, 1993.