VirtualQuery
is pretty much your only option. It should be fairly efficient:
The function determines the attributes of the first page in the region and then scans subsequent pages until it scans the entire range of pages or until it encounters a page with a nonmatching set of attributes.
So you'd start by calling it at the beginning of the range that you care about, and you'd get back a single chunk of pages. The next call would start right after that chunk, and the next one after that, and so on.
Here's a completely untested function that would populate an array of MEMORY_BASIC_INFORMATION
structures:
int EnumVirtualAllocations(const void* ptr, size_t length, MEMORY_BASIC_INFORMATION* info, int size)
{
const void* end = (const void*)((const char*)ptr + length);
int index = 0;
while (index < size && ptr < end &&
VirtualQuery(ptr, &info[index], sizeof(*info)) == sizeof(*info))
{
MEMORY_BASIC_INFORMATION* i = &info[index];
if (i->State != MEM_FREE) index++;
ptr = (const void*)((const char*)(i->BaseAddress) + i->RegionSize);
}
return index;
}