COM provides
API that allows user code to monitor COM memory allocations and deallocations
(see
IMalloc
,
IMallocSpy
,
,
).
This includes memory allocations made explicitly by user code
as well as memory allocations made implicitly by COM runtime
on behalf of the user.
Experiment shows that on Windows 2000/XP some memory still remains
allocated even after COM has been shut down by calling
. This memory is eventually
freed by program termination code in
.
Behavior on Windows XP is the same.
This makes detection of COM memory leaks difficult. At no moment in our program we can say "OK, all COM memory should be released by now. If anything is not released, it is definitely a leak".
Life used to be easier on Windows NT. On Windows NT
released all the memory
allocated by COM runtime. Thus, a good time to check
for leaks was right after
.
Anything not released by then could be safely declared a leak.
Not anymore.
Windows 2000 introduces a new system DLL named clbcatq.dll
.
This DLL is loaded by COM runtime when you call
(and of course porbably in some other cases too). clbcatq.dll
startup code located in
allocates some COM memory using
. This memory is released by clbcatq.dll
shutdown code, also in
.
The caveat is that
does
not unload clbcatq.dll
! This appears to contradict
statements from
help page:
CoUninitialize
closes the COM library on the current thread,
unloads all DLLs loaded by the thread, frees any other
resources that the thread maintains, and forces all
RPC connections on the thread to close.
|
Anyway, clbcatq.dll
DLL gets unloaded
by
upon program termination. The memory is released
at this time using
.
MallocSpyTest
is a console application.
It tries to create an instance of a COM coclass specified on the command
line. It also implements IMallocSpy
interface and prints
to standard output information about all COM alocations and deallocations.
After
it tries to revoke
malloc spy, and displays list of memory blocks that are not freed.
Test project can be invoked as follows:
MallocSpyTest ADODB.Recordset
You can use any COM coclass you like instead of ADODB.Recordset
.
C:\TEMP>MallocSpyTest.exe ADODB.Recordset CoInitialize() returns 0x00000000 CoRegisterMallocSpy() returns 0x00000000 CLSIDFromProgID() returns 0x00000000 MALLOC SPY: 0x00139E90 - 216 bytes allocated MALLOC SPY: 0x00138650 - 16 bytes allocated MALLOC SPY: 0x00139F70 - 16 bytes allocated CoCreateInstance() returns 0x00000000 Object released MALLOC SPY: 0x00138650 - freed, 2 blocks remaining MALLOC SPY: 0x00139F70 - freed, 1 blocks remaining MALLOC SPY: 0x00139E90 - freed, 0 blocks remaining Called CoUninitialize() CoRevokeMallocSpy() returns 0x00000000 C:\TEMP> |
However, results on Windows 2000 are astonishing:
C:\Ivan\cpp\MallocSpyTest>MallocSpyTest ADODB.Recordset CoInitialize() returns 0x00000000 CoRegisterMallocSpy() returns 0x00000000 MALLOC SPY: 0x0013BBB0 - 28 bytes allocated more allocations... CLSIDFromProgID() returns 0x00000000 [...] CoCreateInstance() returns 0x00000000 Object released [...] MALLOC SPY: 0x0013C150 - freed, 18 blocks remaining [...] Called CoUninitialize() CoRevokeMallocSpy() returns 0x80070005 MALLOC SPY: detected memory leaks! 0x0013A8F8 - 44 bytes: 98 B1 5A 77 08 BC 13 00 01 00 00 00 01 00 11 00 ..Zw............ more leaks... MALLOC SPY: 0x0013C0C0 - freed, 11 blocks remaining [...] MALLOC SPY: 0x0013BED0 - freed, 1 blocks remaining |
Take a note of two things:
CoUninitialize()
CoRevokeMallocSpy()
fails with error
code is 0x8007005
which stands for E_ACCESSDENIED
. I.e., we are
not allowed to revoke the spy, despite the fact that COM has been shut down. This obviously
makes using spy very problematic.
Since our spy continues to work while the program is being
terminated, and continues to call
while inside
, weird things happen.
is not really
designed to work during
.
One side effect of this is that if you redirect output of
MallocSpyTest
to a file (as in
MallocSpyTest ADODB.Recordset >out.txt
),
last lines of output won't go to the file.
Results on XP are similar to those on 2000, main difference is more allocations.