I'm curious, what do you plan on doing with this information?
There is a windbg extension, !address, which can get you this information, if you don't need code to do it. Scripting the debugger will probably be much more reliable to get this info.
VirtualQuery can't return this information to you on its own, since it has no idea why user mode code requested the memory. You need to use it with other information sources to get this info, and there may still be some error cases.
First, you should filter only by MEM_PRIVATE memory . . . heap, stack, and static allocations (provided they've been modified) should fall within that range.
Static allocations (globals, etc.) should be at an address with a loaded module. You can use PSAPI to determine if the address is within a loaded module, for example, calling EnumProcessModules and then GetModuleInformation.
Stack values, you can use the toolhelp API to determine if the memory location is in a stack. CreateToolhelp32Snapshot with TH32CS_SNAPSHOT to get threads in the target process, then GetThreadContext and check if the resulting stack pointer is within the segment.
I'm don't know of a good way to walk heaps from outside the process. Toolhelp snaps a heap list but doesn't give you a good set of bounds for the heap memory. From within the process, you can use GetProcessHeaps to walk the list of heaps, and then call HeapValidate to dtermie if the memory location is within the heap.