Upravit

Sdílet prostřednictvím


Traversing the Heap List

The following example obtains a list of heaps for the current process. It takes a snapshot of the heaps using the CreateToolhelp32Snapshot function, and then walks through the list using the Heap32ListFirst and Heap32ListNext functions. For each heap, it uses the Heap32First and Heap32Next functions to walk the heap blocks.

Note

Heap32First and Heap32Next are inefficient, particularly for large heaps. However, they are useful for querying other processes where you'd typically have to inject a thread into the other process to gather the information (these APIs do this for you).

See the second example for an equivalent, much more efficient, alternative that uses HeapWalk instead of Heap32First and Heap32Next. Note that HeapWalk can only be used for the same process.

#include <windows.h>
#include <tlhelp32.h>
#include <stdio.h>

int main( void )
{
   HEAPLIST32 hl;
   
   HANDLE hHeapSnap = CreateToolhelp32Snapshot(TH32CS_SNAPHEAPLIST, GetCurrentProcessId());
   
   hl.dwSize = sizeof(HEAPLIST32);
   
   if ( hHeapSnap == INVALID_HANDLE_VALUE )
   {
      printf ("CreateToolhelp32Snapshot failed (%d)\n", GetLastError());
      return 1;
   }
   
   if( Heap32ListFirst( hHeapSnap, &hl ) )
   {
      do
      {
         HEAPENTRY32 he;
         ZeroMemory(&he, sizeof(HEAPENTRY32));
         he.dwSize = sizeof(HEAPENTRY32);

         if( Heap32First( &he, GetCurrentProcessId(), hl.th32HeapID ) )
         {
            printf( "\nHeap ID: %d\n", hl.th32HeapID );
            do
            {
               printf( "Block size: %d\n", he.dwBlockSize );
               
               he.dwSize = sizeof(HEAPENTRY32);
            } while( Heap32Next(&he) );
         }
         hl.dwSize = sizeof(HEAPLIST32);
      } while (Heap32ListNext( hHeapSnap, &hl ));
   }
   else printf ("Cannot list first heap (%d)\n", GetLastError());
   
   CloseHandle(hHeapSnap); 

   return 0;
}

The following code snippet uses the HeapWalk function to walk the process heaps, producing identical output to the previous example, but much more efficiently:

#include <windows.h>
#include <stdio.h>

int main( void )
{
    DWORD heapIndex;
    DWORD heapCount = 0;
    PHANDLE heaps = NULL;
    while (TRUE)
    {
        DWORD actualHeapCount = GetProcessHeaps(heapCount, heaps);
        if (actualHeapCount <= heapCount)
        {
            break;
        }
        heapCount = actualHeapCount;
        free(heaps);
        heaps = (HANDLE*)malloc(heapCount * sizeof(HANDLE));
        if (heaps == NULL)
        {
            printf("Unable to allocate memory for list of heaps\n");
            return 1;
        }
    }

    for (heapIndex = 0; heapIndex < heapCount; heapIndex++)
    {
        PROCESS_HEAP_ENTRY entry;

        printf("Heap ID: %d\n", (DWORD)(ULONG_PTR)heaps[heapIndex]);
        entry.lpData = NULL;
        while (HeapWalk(heaps[heapIndex], &entry))
        {
            // Heap32First and Heap32Next ignore entries
            // with the PROCESS_HEAP_REGION flag
            if (!(entry.wFlags & PROCESS_HEAP_REGION))
            {
                printf("Block size: %d\n", entry.cbData + entry.cbOverhead);
            }
        }
    }

    free(heaps);
    return 0;
}

Walking a heap with the HeapWalk function is roughly linear in the size of the heap, whereas walking a heap with the Heap32Next function is roughly quadratic in the size of the heap. Even for a modest heap with 10,000 allocations, HeapWalk runs 10,000 times faster than Heap32Next while providing more detailed information. The difference in performance becomes even more dramatic as the heap size increases.

For a more detailed example of walking the heap with the HeapWalk function, see Enumerating a Heap.