Tracing memory leaks with ATL

Do you remember memory-leak tracemessages from MFC ? The debug heap of Visual C in
conjunction with Microsoft's Foundation class library (MFC) dumps the memory of all
objects that have not been freed at the time when the program exits to the trace window of
Visual Studio.

Two years ago I switched over to ATL programming. In most cases I do not mix ATL and
MFC libraries within the same project. Suddenly I didn't get the familiar

message any more which allowed me to double-click and jump directly to the code where
the object had been created. Since then I have tried various debugging aids provided by
ATL but was never quite happy. A year ago I decided to extend ATL to bring back this
missing feature. This article is the result of some cool template hacking.

Microsoft's debug heap implementation allows programmers to associate a line number,
filename and some other information with each memory allocation. If it would be possible
to overload all memory allocation functions with the respective debug-heap allocation
functions and if I could associate filename and linenumber with each allocation I
would get what I wanted. A peak at the MFC source code showed how to do this for new() and
malloc() by adding the following to each *.CPP file:

Unfortunately you can't have a static variable in a *.H file, so this didn't work for
ATL- and user-defined templates. Besides that I didn't want to change any ATL files
provided by Microsoft.

ATL implements a class factory for each COM object. The class factory is responsible
for creating an instance of the associated object via a call to new()
With the goal to extend or replace ATL's class factory code I started looking through the
ATL code for hooks and macros that I could overload. Fortunately, the designers of
ATL defined the macros DECLARE_AGGREGATABLE and DECLARE_NOT_AGGREGATABLE to define
which kind of classfactory should be used for a given object. Through several intermediate
steps these macros make an instance of "CComCreator", which creates an
instances of the user-defined COM object via a call to new().

Without modifying MS's code it was impossible to overload new() and I decided to create
my own CComCreator class:

Now the challenge was to get the exact filename and line number where the object was
defined. Using the macros '__FILE__' and '__LINE__' didn't do much good because they were
always pointing to the file that implemented the new CComCreator class.

After many experiments I settled on the following solution: If the user wants to use
the debug heap functions for the COM object he/she has to include
DECLARE_xxx_AGGREGATABLE_DBG(CMyClass) inside the class definition of the object:

As you can see I am adding two static functions to the class that return the name and
location of the file where 'DECLARE_xxx_AGGREGATABLE_DBG' is
defined (in other words the *.H file). The debug version of CComCreator in return accesses
these functions via a call to 'BASE::_GetFileName();' and 'BASE::_GetLineNo()'.

The source code contains some more macros but they work very similar. I have been using
the ATL debug heap for quite a while and I have always been very happy to discover memory
leaks right away. I hope it will also be useful to other ATL programmers as well.

How to use it:

First thing to do is to add "DebugHeap.cpp" to your project and
include "AtlDebugHeap.h" in your header file list. As the last statement
in DllMain() add the following call

DebugHeap_DllMain(hInstance, dwReason, lpReserved);

which will initialize the debug heap at startup time and dump the leaks at the end. If
you want to trace memory leaks created via calls to new() and malloc() add the following
to each *.cpp file:

Add DECLARE_AGGREGATABLE_DBG(CYourClass) or
DECLARE_NOT_AGGREGATABLE_DBG(CYourClass) to each COM object and you are done.

Example:

I have created a small sample project. The project creates an OCX (ProgID: AtlDbgHeapDemo.ATlDebugHeapDemoObj.1)
with a memory leak in

CAtlDebugHeapDemoObj::FinalConstruct( )>

Search for "@@@$$$@@@" to find
all the changes I have made. In order to test it you have to make an instance of the OCX
with a program running under the debugger (otherwise you won't get the trace
messages). I tried to use MS's testcontainer but it does not shut down if the ref-count of
any of the objects is not zero. Instead I have successfully tested it with VB and IE5.

In order to use IE5, compile the code, select IE under Program/Settings/Executable.
Once IE comes up, load the *.HTML file from the source directory. Shut down IE and you
should something similar to the following in the trace window of the debugger.

As you can see the program contains three leaks . The last leak points
to the line where I have added the 'DECLARE_AGGREGATABLE_DBG(CAtlDebugHeapDemoObj)'
statement. The debugger shows the filename and line number of each allocation and if you
click on it, the appropriate file gets opened and the cursor jumps to the given line
number.

Top White Papers and Webcasts

Today's enterprise datacenter can be one of the most complex business environments with dozens (for smaller business), hundreds (for larger and midsize business), even thousands (for hyperscale businesses) of servers that must be managed and monitored. At this level, just managing the cords can be challenging — let alone keeping up with the growing need for more agility and scalability within the datacenter. Simply put, companies are aggressively looking for less complexity and more agility from their …

Entire organizations suffer when their networks can't keep up and new opportunities are put on hold. Waiting on service providers isn't good business. In these examples, learn how to simplify network management so that your organization can better manage costs, adapt quickly to business demands, and seize market opportunities when they arise.