The QueryVirtualMemoryInformation function fails in 32-bit mode because the WIN32_MEMORY_REGION_INFORMATION structure is defined incorrectly.

RoflCopter4 10 Reputation points
2024-06-04T11:29:47.0133333+00:00

For the record, I have Windows 11, "Version 10.0.22631 Build 22631".

The WIN32_MEMORY_REGION_INFORMATION structure is defined in "memoryapi.h" as the following:

typedef struct WIN32_MEMORY_REGION_INFORMATION {
    PVOID AllocationBase;
    ULONG AllocationProtect;
    union {
        ULONG Flags;
        struct {
            ULONG Private : 1;
            ULONG MappedDataFile : 1;
            ULONG MappedImage : 1;
            ULONG MappedPageFile : 1;
            ULONG MappedPhysical : 1;
            ULONG DirectMapped : 1;
            ULONG Reserved : 26;
        } DUMMYSTRUCTNAME;
    } DUMMYUNIONNAME;
    SIZE_T RegionSize;
    SIZE_T CommitSize;
} WIN32_MEMORY_REGION_INFORMATION;

It is 32 bytes in size in 64-bit mode, and 20 bytes in size in 32-bit mode. However, when supplied to the function QueryVirtualMemoryInformation in 32-bit mode, the call fails with the error ERROR_BAD_LENGTH. If one replaces the first two fields with 64-bit types, however, the function call succeeds, and indicates that it wrote 28 bytes to the structure.

I don't know if this is intended or not, but as it is the provided header file is broken. Perhaps the error lies with me only, I can't say. If not, there either ought to be documentation stating that the function does not work in 32-bit mode, or else the structure definition ought to be fixed.

I'm also not at all confident this is a sensible place to report this but I honestly didn't know where else to do it. I'm aware that this isn't a question. I posted it to the Windows Feedback Hub and it naturally got buried, especially with the lack of information forced by the low maximum character count.

I've written this which should reproduce the problem. In 64-bit mode the program has no problems. When compiled in 32-bit mode the code using the official structure fails, and the code which replaces the types with UINT64 succeeds. https://github.com/roflcopter4/QueryVirtualMemoryInformation_BugRepro

Windows
Windows
A family of Microsoft operating systems that run across personal computers, tablets, laptops, phones, internet of things devices, self-contained mixed reality headsets, large collaboration screens, and other devices.
4,993 questions
Windows API - Win32
Windows API - Win32
A core set of Windows application programming interfaces (APIs) for desktop and server applications. Previously known as Win32 API.
2,492 questions
C++
C++
A high-level, general-purpose programming language, created as an extension of the C programming language, that has object-oriented, generic, and functional features in addition to facilities for low-level memory manipulation.
3,608 questions
{count} vote

1 answer

Sort by: Most helpful
  1. RLWA32 42,366 Reputation points
    2024-06-04T14:53:49.0333333+00:00

    The 32-bit failure reproduced for me on Win 10 22H2.

    Instead of checking the memory for a variable I suggest you use VirtualAlloc so that you can specific the memory size and the page protection values. That way you can determine if the information returned in the structure makes sense.

    In my quick and dirty 32-bit test I found that adding the additional 8 bytes to the end of the structure made sense since it kept the size of pointers consistent at 4 bytes and the other memory attributes agreed with the call to VirtualAlloc for region size and protect values.

    Update:

    This is the code I used-

    #define WIN32_LEAN_AND_MEAN
    #include <Windows.h>
    
    #include <stdio.h>
    #include <tchar.h>
    
    DWORD ShowLastError(LPCTSTR pszText, DWORD dwError = GetLastError());
    
    typedef struct _MY_WIN32_MEMORY_REGION_INFORMATION  : WIN32_MEMORY_REGION_INFORMATION {
    #ifndef _WIN64
        ULONG Extra1;
        ULONG Extra2;
    #endif
    } MY_WIN32_MEMORY_REGION_INFORMATION;
    
    int main()
    {
        SIZE_T ReturnSize;
        MY_WIN32_MEMORY_REGION_INFORMATION mi;
    
        PVOID pv = VirtualAlloc(NULL, 8192, MEM_RESERVE, PAGE_READWRITE);
        _tprintf_s(_T("Memory Reserved by VirtualAlloc at 0x%p\n"), pv);
    
        if (QueryVirtualMemoryInformation(GetCurrentProcess(), pv, MemoryRegionInfo, &mi, sizeof mi, &ReturnSize))
        {
            _tprintf_s(_T("Allocation BaseAddress is 0x%p\n"), mi.AllocationBase);
            _tprintf_s(_T("Region Size is %Iu, Commit Size is %Iu\n"), mi.RegionSize, mi.CommitSize);
            _tprintf_s(_T("Page Protect is %X\n"), mi.AllocationProtect);
        }
        else
            ShowLastError(_T("QueryVirtualMemoryInformation"));
    
        PBYTE pCommit = (PBYTE)pv + 4096;
        PVOID pv2 = VirtualAlloc(pCommit, 4096, MEM_COMMIT, PAGE_READWRITE);
    
        _tprintf_s(_T("\nMemory Committed by VirtualAlloc at 0x%p\n"), pCommit);
        if (QueryVirtualMemoryInformation(GetCurrentProcess(), pv2, MemoryRegionInfo, &mi, sizeof mi, &ReturnSize))
        {
            _tprintf_s(_T("Allocation BaseAddress is 0x%p\n"), mi.AllocationBase);
            _tprintf_s(_T("Region Size is %Iu, Commit Size is %Iu\n"), mi.RegionSize, mi.CommitSize);
            _tprintf_s(_T("Page Protect is %X\n"), mi.AllocationProtect);
        }
        else
            ShowLastError(_T("QueryVirtualMemoryInformation"));
    
        PVOID pv3 = VirtualAlloc(pv, 4096, MEM_COMMIT, PAGE_READWRITE);
        _tprintf_s(_T("\nMemory Committed by VirtualAlloc at 0x%p\n"), pv3);
    
        if (QueryVirtualMemoryInformation(GetCurrentProcess(), pv3, MemoryRegionInfo, &mi, sizeof mi, &ReturnSize))
        {
            _tprintf_s(_T("Allocation BaseAddress is 0x%p\n"), mi.AllocationBase);
            _tprintf_s(_T("Region Size is %Iu, Commit Size is %Iu\n"), mi.RegionSize, mi.CommitSize);
            _tprintf_s(_T("Page Protect is %X\n"), mi.AllocationProtect);
        }
        else
            ShowLastError(_T("QueryVirtualMemoryInformation"));
    
    
        return 0;
    }
    
    DWORD ShowLastError(LPCTSTR pszText, DWORD dwError)
    {
        LPTSTR pszMsg = NULL;
    
        FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
            NULL, dwError, 0, (LPTSTR)&pszMsg, 0, NULL);
    
        _tprintf_s(_T("%s : %s (%u)\n"), (pszText != NULL ? pszText : _T("Error was")), pszMsg, dwError);
    
        LocalFree(pszMsg);
    
        return dwError;
    }
    
    1 person found this answer helpful.