/*++ Copyright (c) 1990 Microsoft Corporation Module Name: gmem.c Abstract: This module contains the Win32 Global Memory Management APIs Author: Steve Wood (stevewo) 24-Sep-1990 Revision History: --*/ #include "basedll.h" #pragma hdrstop #include "winuserp.h" #include "wowuserp.h" #include PFNWOWGLOBALFREEHOOK pfnWowGlobalFreeHook = NULL; VOID WINAPI RegisterWowBaseHandlers( PFNWOWGLOBALFREEHOOK pfn ) { pfnWowGlobalFreeHook = pfn; } #if i386 #pragma optimize("y",off) #endif HGLOBAL WINAPI GlobalAlloc( UINT uFlags, SIZE_T dwBytes ) { PBASE_HANDLE_TABLE_ENTRY HandleEntry; HANDLE hMem; LPSTR p; ULONG Flags; if (uFlags & ~GMEM_VALID_FLAGS) { SetLastError( ERROR_INVALID_PARAMETER ); return( NULL ); } Flags = 0; if (uFlags & GMEM_ZEROINIT) { Flags |= HEAP_ZERO_MEMORY; } if (!(uFlags & GMEM_MOVEABLE)) { if (uFlags & GMEM_DDESHARE) { Flags |= BASE_HEAP_FLAG_DDESHARE; } p = RtlAllocateHeap( BaseHeap, MAKE_TAG( GMEM_TAG ) | Flags, dwBytes ? dwBytes : 1 ); if (p == NULL) { SetLastError( ERROR_NOT_ENOUGH_MEMORY ); } return p; } p = NULL; RtlLockHeap( BaseHeap ); Flags |= HEAP_NO_SERIALIZE | HEAP_SETTABLE_USER_VALUE | BASE_HEAP_FLAG_MOVEABLE; try { HandleEntry = (PBASE_HANDLE_TABLE_ENTRY)RtlAllocateHandle( &BaseHeapHandleTable, NULL ); if (HandleEntry == NULL) { SetLastError( ERROR_NOT_ENOUGH_MEMORY ); goto Fail; } hMem = (HANDLE)&HandleEntry->Object; if (dwBytes != 0) { p = (LPSTR)RtlAllocateHeap( BaseHeap, MAKE_TAG( GMEM_TAG ) | Flags, dwBytes ); if (p == NULL) { HandleEntry->Flags = RTL_HANDLE_ALLOCATED; RtlFreeHandle( &BaseHeapHandleTable, (PRTL_HANDLE_TABLE_ENTRY)HandleEntry ); HandleEntry = NULL; SetLastError( ERROR_NOT_ENOUGH_MEMORY ); } else { RtlSetUserValueHeap( BaseHeap, HEAP_NO_SERIALIZE, p, hMem ); } } Fail: ; } except (EXCEPTION_EXECUTE_HANDLER) { BaseSetLastNTError( GetExceptionCode() ); } RtlUnlockHeap( BaseHeap ); if (HandleEntry != NULL) { HandleEntry->Object = p; if (p != NULL) { HandleEntry->Flags = RTL_HANDLE_ALLOCATED; } else { HandleEntry->Flags = RTL_HANDLE_ALLOCATED | BASE_HANDLE_DISCARDED; } if (uFlags & GMEM_DISCARDABLE) { HandleEntry->Flags |= BASE_HANDLE_DISCARDABLE; } if (uFlags & GMEM_MOVEABLE) { HandleEntry->Flags |= BASE_HANDLE_MOVEABLE; } if (uFlags & GMEM_DDESHARE) { HandleEntry->Flags |= BASE_HANDLE_SHARED; } p = (LPSTR)hMem; } return( (HANDLE)p ); } HGLOBAL WINAPI GlobalReAlloc( HANDLE hMem, SIZE_T dwBytes, UINT uFlags ) { PBASE_HANDLE_TABLE_ENTRY HandleEntry; HANDLE Handle; LPSTR p; ULONG Flags; if ((uFlags & ~(GMEM_VALID_FLAGS | GMEM_MODIFY)) || ((uFlags & GMEM_DISCARDABLE) && !(uFlags & GMEM_MODIFY)) ) { #if DBG DbgPrint( "*** GlobalReAlloc( %lx ) - invalid flags\n", uFlags ); BaseHeapBreakPoint(); #endif SetLastError( ERROR_INVALID_PARAMETER ); return( NULL ); } Flags = 0; if (uFlags & GMEM_ZEROINIT) { Flags |= HEAP_ZERO_MEMORY; } if (!(uFlags & GMEM_MOVEABLE)) { Flags |= HEAP_REALLOC_IN_PLACE_ONLY; } RtlLockHeap( BaseHeap ); Flags |= HEAP_NO_SERIALIZE; try { if ((ULONG_PTR)hMem & BASE_HANDLE_MARK_BIT) { HandleEntry = (PBASE_HANDLE_TABLE_ENTRY) CONTAINING_RECORD( hMem, BASE_HANDLE_TABLE_ENTRY, Object ); if (!RtlIsValidHandle( &BaseHeapHandleTable, (PRTL_HANDLE_TABLE_ENTRY)HandleEntry )) { #if DBG DbgPrint( "*** GlobalReAlloc( %lx ) - invalid handle\n", hMem ); BaseHeapBreakPoint(); #endif SetLastError( ERROR_INVALID_HANDLE ); hMem = NULL; } else if (uFlags & GMEM_MODIFY) { if (uFlags & GMEM_DISCARDABLE) { HandleEntry->Flags |= BASE_HANDLE_DISCARDABLE; } else { HandleEntry->Flags &= ~BASE_HANDLE_DISCARDABLE; } } else { p = HandleEntry->Object; if (dwBytes == 0) { hMem = NULL; if (p != NULL) { if ((uFlags & GMEM_MOVEABLE) && HandleEntry->LockCount == 0) { if (RtlFreeHeap( BaseHeap, Flags, p )) { HandleEntry->Object = NULL; HandleEntry->Flags |= BASE_HANDLE_DISCARDED; hMem = (HANDLE)&HandleEntry->Object; } } else { #if DBG DbgPrint( "*** GlobalReAlloc( %lx ) - failing with locked handle\n", &HandleEntry->Object ); BaseHeapBreakPoint(); #endif } } else { hMem = (HANDLE)&HandleEntry->Object; } } else { Flags |= HEAP_SETTABLE_USER_VALUE | BASE_HEAP_FLAG_MOVEABLE; if (p == NULL) { p = RtlAllocateHeap( BaseHeap, MAKE_TAG( GMEM_TAG ) | Flags, dwBytes ); if (p != NULL) { RtlSetUserValueHeap( BaseHeap, HEAP_NO_SERIALIZE, p, hMem ); } } else { if (!(uFlags & GMEM_MOVEABLE) && HandleEntry->LockCount != 0 ) { Flags |= HEAP_REALLOC_IN_PLACE_ONLY; } else { Flags &= ~HEAP_REALLOC_IN_PLACE_ONLY; } #pragma prefast(suppress: 308, "Realloc is allowed to lose this pointer") p = RtlReAllocateHeap( BaseHeap, MAKE_TAG( GMEM_TAG ) | Flags, p, dwBytes ); } if (p != NULL) { HandleEntry->Object = p; HandleEntry->Flags &= ~BASE_HANDLE_DISCARDED; } else { hMem = NULL; SetLastError( ERROR_NOT_ENOUGH_MEMORY ); } } } } else if (uFlags & GMEM_MODIFY) { if (uFlags & GMEM_MOVEABLE) { Handle = hMem; if (RtlGetUserInfoHeap( BaseHeap, HEAP_NO_SERIALIZE, (PVOID)hMem, &Handle, NULL )) { if (Handle == hMem || !(Flags & BASE_HEAP_FLAG_MOVEABLE)) { HandleEntry = (PBASE_HANDLE_TABLE_ENTRY)RtlAllocateHandle( &BaseHeapHandleTable, NULL ); if (HandleEntry == NULL) { hMem = NULL; SetLastError( ERROR_NOT_ENOUGH_MEMORY ); } else { dwBytes = RtlSizeHeap( BaseHeap, HEAP_NO_SERIALIZE, hMem ); Flags |= HEAP_SETTABLE_USER_VALUE | BASE_HEAP_FLAG_MOVEABLE; HandleEntry->Object = (PVOID)RtlAllocateHeap( BaseHeap, MAKE_TAG( GMEM_TAG ) | Flags, dwBytes ); if (HandleEntry->Object == NULL) { HandleEntry->Flags = RTL_HANDLE_ALLOCATED; RtlFreeHandle( &BaseHeapHandleTable, (PRTL_HANDLE_TABLE_ENTRY)HandleEntry ); hMem = NULL; SetLastError( ERROR_NOT_ENOUGH_MEMORY ); } else { RtlMoveMemory( HandleEntry->Object, hMem, dwBytes ); RtlFreeHeap( BaseHeap, HEAP_NO_SERIALIZE, hMem ); hMem = (HANDLE)&HandleEntry->Object; HandleEntry->LockCount = 0; HandleEntry->Flags = RTL_HANDLE_ALLOCATED | BASE_HANDLE_MOVEABLE; if (uFlags & GMEM_DISCARDABLE) { HandleEntry->Flags |= BASE_HANDLE_DISCARDABLE; } if ((ULONG_PTR)Handle & GMEM_DDESHARE) { HandleEntry->Flags |= BASE_HANDLE_SHARED; } RtlSetUserValueHeap( BaseHeap, HEAP_NO_SERIALIZE, HandleEntry->Object, hMem ); } } } } } } else { #pragma prefast(suppress: 308, "Realloc is allowed to lose this pointer") hMem = RtlReAllocateHeap( BaseHeap, MAKE_TAG( GMEM_TAG ) | Flags | HEAP_NO_SERIALIZE, (PVOID)hMem, dwBytes ); if (hMem == NULL) { SetLastError( ERROR_NOT_ENOUGH_MEMORY ); } } } except (EXCEPTION_EXECUTE_HANDLER) { hMem = NULL; BaseSetLastNTError( GetExceptionCode() ); } RtlUnlockHeap( BaseHeap ); return( (LPSTR)hMem ); } LPVOID WINAPI GlobalLock( HGLOBAL hMem ) { PBASE_HANDLE_TABLE_ENTRY HandleEntry; LPSTR p; if ((ULONG_PTR)hMem & BASE_HANDLE_MARK_BIT) { RtlLockHeap( BaseHeap ); try { HandleEntry = (PBASE_HANDLE_TABLE_ENTRY) CONTAINING_RECORD( hMem, BASE_HANDLE_TABLE_ENTRY, Object ); if (!RtlIsValidHandle( &BaseHeapHandleTable, (PRTL_HANDLE_TABLE_ENTRY)HandleEntry )) { #if DBG DbgPrint( "*** GlobalLock( %lx ) - invalid handle\n", hMem ); BaseHeapBreakPoint(); #endif SetLastError( ERROR_INVALID_HANDLE ); p = NULL; } else { p = HandleEntry->Object; if (p != NULL) { if (HandleEntry->LockCount++ == GMEM_LOCKCOUNT) { HandleEntry->LockCount--; } } else { SetLastError( ERROR_DISCARDED ); } } } except (EXCEPTION_EXECUTE_HANDLER) { p = NULL; BaseSetLastNTError( GetExceptionCode() ); } RtlUnlockHeap( BaseHeap ); return( p ); } else { if ( (ULONG_PTR)hMem >= SystemRangeStart ) { SetLastError( ERROR_INVALID_HANDLE ); return NULL; } if (IsBadReadPtr( hMem, 1 )) { SetLastError( ERROR_INVALID_HANDLE ); return NULL; } return( (LPSTR)hMem ); } } HANDLE WINAPI GlobalHandle( LPCVOID pMem ) { HANDLE Handle; ULONG Flags; RtlLockHeap( BaseHeap ); try { Handle = NULL; if (!RtlGetUserInfoHeap( BaseHeap, HEAP_NO_SERIALIZE, (LPVOID)pMem, &Handle, &Flags )) { SetLastError( ERROR_INVALID_HANDLE ); } else if (Handle == NULL || !(Flags & BASE_HEAP_FLAG_MOVEABLE)) { Handle = (HANDLE)pMem; } } except (EXCEPTION_EXECUTE_HANDLER) { BaseSetLastNTError( GetExceptionCode() ); } RtlUnlockHeap( BaseHeap ); return( Handle ); } BOOL WINAPI GlobalUnlock( HANDLE hMem ) { PBASE_HANDLE_TABLE_ENTRY HandleEntry; BOOL Result; Result = TRUE; if ((ULONG_PTR)hMem & BASE_HANDLE_MARK_BIT) { RtlLockHeap( BaseHeap ); try { HandleEntry = (PBASE_HANDLE_TABLE_ENTRY) CONTAINING_RECORD( hMem, BASE_HANDLE_TABLE_ENTRY, Object ); if (!RtlIsValidHandle( &BaseHeapHandleTable, (PRTL_HANDLE_TABLE_ENTRY)HandleEntry )) { #if DBG PVOID ImageBase; // // If passed address is NOT part of an image file, then display // a debug message. This prevents apps that call GlobalUnlock // with the return value of LockResource from displaying the // message. // if (!RtlPcToFileHeader( (PVOID)hMem, &ImageBase)) { DbgPrint( "*** GlobalUnlock( %lx ) - invalid handle\n", hMem ); BaseHeapBreakPoint(); } #endif SetLastError( ERROR_INVALID_HANDLE ); } else if (HandleEntry->LockCount-- == 0) { HandleEntry->LockCount++; SetLastError( ERROR_NOT_LOCKED ); Result = FALSE; } else if (HandleEntry->LockCount == 0) { SetLastError( NO_ERROR ); Result = FALSE; } } except (EXCEPTION_EXECUTE_HANDLER) { BaseSetLastNTError( GetExceptionCode() ); } RtlUnlockHeap( BaseHeap ); } return( Result ); } SIZE_T WINAPI GlobalSize( HANDLE hMem ) { PBASE_HANDLE_TABLE_ENTRY HandleEntry; PVOID Handle; ULONG Flags; SIZE_T dwSize; dwSize = MAXULONG_PTR; Flags = 0; RtlLockHeap( BaseHeap ); try { if (!((ULONG_PTR)hMem & BASE_HANDLE_MARK_BIT)) { Handle = NULL; if (!RtlGetUserInfoHeap( BaseHeap, Flags, hMem, &Handle, &Flags )) { } else if (Handle == NULL || !(Flags & BASE_HEAP_FLAG_MOVEABLE)) { dwSize = RtlSizeHeap( BaseHeap, HEAP_NO_SERIALIZE, (PVOID)hMem ); } else { hMem = Handle; } } if ((ULONG_PTR)hMem & BASE_HANDLE_MARK_BIT) { HandleEntry = (PBASE_HANDLE_TABLE_ENTRY) CONTAINING_RECORD( hMem, BASE_HANDLE_TABLE_ENTRY, Object ); if (!RtlIsValidHandle( &BaseHeapHandleTable, (PRTL_HANDLE_TABLE_ENTRY)HandleEntry )) { #if DBG DbgPrint( "*** GlobalSize( %lx ) - invalid handle\n", hMem ); BaseHeapBreakPoint(); #endif SetLastError( ERROR_INVALID_HANDLE ); } else if (HandleEntry->Flags & BASE_HANDLE_DISCARDED) { dwSize = HandleEntry->Size; } else { dwSize = RtlSizeHeap( BaseHeap, HEAP_NO_SERIALIZE, HandleEntry->Object ); } } } except (EXCEPTION_EXECUTE_HANDLER) { BaseSetLastNTError( GetExceptionCode() ); } RtlUnlockHeap( BaseHeap ); if (dwSize == MAXULONG_PTR) { SetLastError( ERROR_INVALID_HANDLE ); return 0; } else { return dwSize; } } UINT WINAPI GlobalFlags( HANDLE hMem ) { PBASE_HANDLE_TABLE_ENTRY HandleEntry; HANDLE Handle; ULONG Flags; UINT uFlags; uFlags = GMEM_INVALID_HANDLE; RtlLockHeap( BaseHeap ); try { if (!((ULONG_PTR)hMem & BASE_HANDLE_MARK_BIT)) { Handle = NULL; Flags = 0; if (!RtlGetUserInfoHeap( BaseHeap, Flags, hMem, &Handle, &Flags )) { } else if (Handle == NULL || !(Flags & BASE_HEAP_FLAG_MOVEABLE)) { uFlags = 0; } else { hMem = Handle; } } if ((ULONG_PTR)hMem & BASE_HANDLE_MARK_BIT) { HandleEntry = (PBASE_HANDLE_TABLE_ENTRY) CONTAINING_RECORD( hMem, BASE_HANDLE_TABLE_ENTRY, Object ); if (RtlIsValidHandle( &BaseHeapHandleTable, (PRTL_HANDLE_TABLE_ENTRY)HandleEntry )) { uFlags = HandleEntry->LockCount & GMEM_LOCKCOUNT; if (HandleEntry->Flags & BASE_HANDLE_DISCARDED) { uFlags |= GMEM_DISCARDED; } if (HandleEntry->Flags & BASE_HANDLE_DISCARDABLE) { uFlags |= GMEM_DISCARDABLE; } if (HandleEntry->Flags & BASE_HANDLE_SHARED) { uFlags |= GMEM_DDESHARE; } } } if (uFlags == GMEM_INVALID_HANDLE) { #if DBG DbgPrint( "*** GlobalFlags( %lx ) - invalid handle\n", hMem ); BaseHeapBreakPoint(); #endif SetLastError( ERROR_INVALID_HANDLE ); } } except (EXCEPTION_EXECUTE_HANDLER) { BaseSetLastNTError( GetExceptionCode() ); } RtlUnlockHeap( BaseHeap ); return( uFlags ); } HGLOBAL WINAPI GlobalFree( HGLOBAL hMem ) { PBASE_HANDLE_TABLE_ENTRY HandleEntry; LPSTR p; try { if (pfnWowGlobalFreeHook != NULL) { if (!(*pfnWowGlobalFreeHook)(hMem)) { return NULL; } } if (!((ULONG_PTR)hMem & BASE_HANDLE_MARK_BIT)) { if (RtlFreeHeap( BaseHeap, 0, (PVOID)hMem )) { return NULL; } else { SetLastError( ERROR_INVALID_HANDLE ); return hMem; } } } except (EXCEPTION_EXECUTE_HANDLER) { BaseSetLastNTError( GetExceptionCode() ); return hMem; } RtlLockHeap( BaseHeap ); try { if ((ULONG_PTR)hMem & BASE_HANDLE_MARK_BIT) { HandleEntry = (PBASE_HANDLE_TABLE_ENTRY) CONTAINING_RECORD( hMem, BASE_HANDLE_TABLE_ENTRY, Object ); if (!RtlIsValidHandle( &BaseHeapHandleTable, (PRTL_HANDLE_TABLE_ENTRY)HandleEntry )) { #if DBG DbgPrint( "*** GlobalFree( %lx ) - invalid handle\n", hMem ); BaseHeapBreakPoint(); #endif SetLastError( ERROR_INVALID_HANDLE ); p = NULL; } else { #if DBG if (HandleEntry->LockCount != 0) { DbgPrint( "BASE: GlobalFree called with a locked object.\n" ); BaseHeapBreakPoint(); } #endif p = HandleEntry->Object; RtlFreeHandle( &BaseHeapHandleTable, (PRTL_HANDLE_TABLE_ENTRY)HandleEntry ); if (p == NULL) { hMem = NULL; } } } else { p = (LPSTR)hMem; } if (p != NULL) { if (RtlFreeHeap( BaseHeap, HEAP_NO_SERIALIZE, p )) { hMem = NULL; } else { SetLastError( ERROR_INVALID_HANDLE ); } } } except (EXCEPTION_EXECUTE_HANDLER) { BaseSetLastNTError( GetExceptionCode() ); } RtlUnlockHeap( BaseHeap ); return( hMem ); } SIZE_T WINAPI GlobalCompact( DWORD dwMinFree ) { return RtlCompactHeap( BaseHeap, 0 ); } VOID WINAPI GlobalFix( HGLOBAL hMem ) { if (hMem != (HGLOBAL)-1) { GlobalLock( hMem ); } return; } VOID WINAPI GlobalUnfix( HGLOBAL hMem ) { if (hMem != (HGLOBAL)-1) { GlobalUnlock( hMem ); } return; } LPVOID WINAPI GlobalWire( HGLOBAL hMem ) { return GlobalLock( hMem ); } BOOL WINAPI GlobalUnWire( HGLOBAL hMem ) { return GlobalUnlock( hMem ); } VOID WINAPI GlobalMemoryStatus( LPMEMORYSTATUS lpBuffer ) { DWORD NumberOfPhysicalPages; SYSTEM_PERFORMANCE_INFORMATION PerfInfo; VM_COUNTERS VmCounters; QUOTA_LIMITS QuotaLimits; NTSTATUS Status; PPEB Peb; PIMAGE_NT_HEADERS NtHeaders; DWORDLONG Memory64; Status = NtQuerySystemInformation( SystemPerformanceInformation, &PerfInfo, sizeof(PerfInfo), NULL); ASSERT(NT_SUCCESS(Status)); lpBuffer->dwLength = sizeof( *lpBuffer ); // // Capture the number of physical pages as it can change dynamically. // If it goes up or down in the middle of this routine, the results may // look strange (ie: available > total, etc), but it will quickly // right itself. // NumberOfPhysicalPages = USER_SHARED_DATA->NumberOfPhysicalPages; #if defined(BUILD_WOW6432) // // Convert the number of physical pages from the native system to // the emulation system. // NumberOfPhysicalPages = NumberOfPhysicalPages * (Wow64GetSystemNativePageSize() / BASE_SYSINFO.PageSize); #endif // // Determine the memory load. < 100 available pages is 100 // Otherwise load is ((TotalPhys - AvailPhys) * 100) / TotalPhys // if (PerfInfo.AvailablePages < 100) { lpBuffer->dwMemoryLoad = 100; } else { lpBuffer->dwMemoryLoad = ((DWORD)(NumberOfPhysicalPages - PerfInfo.AvailablePages) * 100) / NumberOfPhysicalPages; } // // Determine the physical memory sizes. // Memory64 = (DWORDLONG)NumberOfPhysicalPages * BASE_SYSINFO.PageSize; lpBuffer->dwTotalPhys = (SIZE_T) __min(Memory64, MAXULONG_PTR); Memory64 = ((DWORDLONG)PerfInfo.AvailablePages * (DWORDLONG)BASE_SYSINFO.PageSize); lpBuffer->dwAvailPhys = (SIZE_T) __min(Memory64, MAXULONG_PTR); if (gpTermsrvAdjustPhyMemLimits) { gpTermsrvAdjustPhyMemLimits(&(lpBuffer->dwTotalPhys), &(lpBuffer->dwAvailPhys), BASE_SYSINFO.PageSize); } // // Zero returned values in case the query process fails. // RtlZeroMemory (&QuotaLimits, sizeof (QUOTA_LIMITS)); RtlZeroMemory (&VmCounters, sizeof (VM_COUNTERS)); Status = NtQueryInformationProcess (NtCurrentProcess(), ProcessQuotaLimits, &QuotaLimits, sizeof(QUOTA_LIMITS), NULL); ASSERT(NT_SUCCESS(Status)); Status = NtQueryInformationProcess (NtCurrentProcess(), ProcessVmCounters, &VmCounters, sizeof(VM_COUNTERS), NULL); ASSERT(NT_SUCCESS(Status)); // // Determine the total page file space with respect to this process. // Memory64 = __min(PerfInfo.CommitLimit, QuotaLimits.PagefileLimit); Memory64 *= BASE_SYSINFO.PageSize; lpBuffer->dwTotalPageFile = (SIZE_T)__min(Memory64, MAXULONG_PTR); // // Determine remaining page file space with respect to this process. // Memory64 = __min(PerfInfo.CommitLimit - PerfInfo.CommittedPages, QuotaLimits.PagefileLimit - VmCounters.PagefileUsage); Memory64 *= BASE_SYSINFO.PageSize; lpBuffer->dwAvailPageFile = (SIZE_T) __min(Memory64, MAXULONG_PTR); lpBuffer->dwTotalVirtual = (BASE_SYSINFO.MaximumUserModeAddress - BASE_SYSINFO.MinimumUserModeAddress) + 1; lpBuffer->dwAvailVirtual = lpBuffer->dwTotalVirtual - VmCounters.VirtualSize; #if !defined(_WIN64) // // Lie about available memory if application can't handle large (>2GB) addresses // Peb = NtCurrentPeb(); NtHeaders = RtlImageNtHeader( Peb->ImageBaseAddress ); if (NtHeaders && !(NtHeaders->FileHeader.Characteristics & IMAGE_FILE_LARGE_ADDRESS_AWARE)) { if (BASE_SYSINFO.MaximumUserModeAddress > 0x7FFEFFFF) { // // Booted /3GB, but the application can't handle large virtual // addresses so remove the portion above 2GB. Note this portion // is variable from zero up to 1GB (depending on the /USERVA value). // lpBuffer->dwAvailVirtual -= (BASE_SYSINFO.MaximumUserModeAddress - 0x7FFEFFFF); } if (lpBuffer->dwTotalPhys > 0x7FFFFFFF) { lpBuffer->dwTotalPhys = 0x7FFFFFFF; } if (lpBuffer->dwAvailPhys > 0x7FFFFFFF) { lpBuffer->dwAvailPhys = 0x7FFFFFFF; } if (lpBuffer->dwTotalVirtual > 0x7FFFFFFF) { lpBuffer->dwTotalVirtual = 0x7FFFFFFF; } if (lpBuffer->dwAvailVirtual > 0x7FFFFFFF) { lpBuffer->dwAvailVirtual = 0x7FFFFFFF; } } #endif return; } PVOID WINAPI VirtualAlloc( PVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect ) { return VirtualAllocEx( NtCurrentProcess(), lpAddress, dwSize, flAllocationType, flProtect ); } BOOL WINAPI VirtualFree( LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType ) { return VirtualFreeEx(NtCurrentProcess(),lpAddress,dwSize,dwFreeType); } PVOID WINAPI VirtualAllocEx( HANDLE hProcess, PVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect ) { NTSTATUS Status; if (lpAddress != NULL && (ULONG_PTR)lpAddress < BASE_SYSINFO.AllocationGranularity) { SetLastError( ERROR_INVALID_PARAMETER ); return( NULL ); } try { Status = NtAllocateVirtualMemory( hProcess, &lpAddress, 0, &dwSize, flAllocationType, flProtect ); } except( EXCEPTION_EXECUTE_HANDLER ) { Status = GetExceptionCode(); } if (NT_SUCCESS( Status )) { return( lpAddress ); } else { BaseSetLastNTError( Status ); return( NULL ); } } BOOL WINAPI VirtualFreeEx( HANDLE hProcess, LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType ) { NTSTATUS Status; if ( (dwFreeType & MEM_RELEASE ) && dwSize != 0 ) { BaseSetLastNTError( STATUS_INVALID_PARAMETER ); return FALSE; } Status = NtFreeVirtualMemory( hProcess, &lpAddress, &dwSize, dwFreeType ); if (NT_SUCCESS( Status )) { return( TRUE ); } else { if (Status == STATUS_INVALID_PAGE_PROTECTION) { if (hProcess == NtCurrentProcess()) { // // Unlock any pages that were locked with MmSecureVirtualMemory. // This is useful for SANs. // if (RtlFlushSecureMemoryCache(lpAddress, dwSize)) { Status = NtFreeVirtualMemory( hProcess, &lpAddress, &dwSize, dwFreeType ); if (NT_SUCCESS( Status )) { return( TRUE ); } } } } BaseSetLastNTError( Status ); return( FALSE ); } } BOOL WINAPI VirtualProtect( PVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect ) { return VirtualProtectEx( NtCurrentProcess(), lpAddress, dwSize, flNewProtect, lpflOldProtect ); } BOOL WINAPI VirtualProtectEx( HANDLE hProcess, PVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect ) { NTSTATUS Status; Status = NtProtectVirtualMemory( hProcess, &lpAddress, &dwSize, flNewProtect, lpflOldProtect ); if (NT_SUCCESS( Status )) { return( TRUE ); } else { if (Status == STATUS_INVALID_PAGE_PROTECTION) { if (hProcess == NtCurrentProcess()) { // // Unlock any pages that were locked with MmSecureVirtualMemory. // This is useful for SANs. // if (RtlFlushSecureMemoryCache(lpAddress, dwSize)) { Status = NtProtectVirtualMemory( hProcess, &lpAddress, &dwSize, flNewProtect, lpflOldProtect ); if (NT_SUCCESS( Status )) { return( TRUE ); } } } } BaseSetLastNTError( Status ); return( FALSE ); } } SIZE_T WINAPI VirtualQuery( LPCVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer, SIZE_T dwLength ) { return VirtualQueryEx( NtCurrentProcess(), lpAddress, (PMEMORY_BASIC_INFORMATION)lpBuffer, dwLength ); } SIZE_T WINAPI VirtualQueryEx( HANDLE hProcess, LPCVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer, SIZE_T dwLength ) { NTSTATUS Status; SIZE_T ReturnLength; Status = NtQueryVirtualMemory( hProcess, (LPVOID)lpAddress, MemoryBasicInformation, (PMEMORY_BASIC_INFORMATION)lpBuffer, dwLength, &ReturnLength ); if (NT_SUCCESS( Status )) { return( ReturnLength ); } else { BaseSetLastNTError( Status ); return( 0 ); } } BOOL WINAPI VirtualLock( LPVOID lpAddress, SIZE_T dwSize ) /*++ Routine Description: This API may be used to lock the specified range of the processes address space into memory. This range is present whenever the application is running. All pages covered by the range must be commited. VirtialLock is in now way related to LocalLock or GlobalLock. It does not perform a handle translation. Its function is to lock memory in the "working set" of the calling process. Note that the specified range is used to compute the range of pages covered by the lock. A 2 byte lock that straddles a page boundry ends up locking both of the pages covered by the range. Also note that calls to VirtualLock do not nest. Arguments: lpAddress - Supplies the base address of the region being locked. dwSize - Supplies the number of bytes being locked. Return Value: TRUE - The operation was was successful. FALSE - The operation failed. Extended error status is available using GetLastError. --*/ { NTSTATUS Status; PVOID BaseAddress; SIZE_T RegionSize; BOOL ReturnValue; ReturnValue = TRUE; BaseAddress = lpAddress; RegionSize = dwSize; Status = NtLockVirtualMemory( NtCurrentProcess(), &lpAddress, &RegionSize, MAP_PROCESS ); if ( !NT_SUCCESS(Status) ) { BaseSetLastNTError(Status); ReturnValue = FALSE; } return ReturnValue; } BOOL WINAPI VirtualUnlock( LPVOID lpAddress, SIZE_T dwSize ) /*++ Routine Description: This API may be used to unlock the specified range of the processes address space from memory. This call is used to reveres the effects of a previous call to VirtualLock. The range specified need not match a range passed to a previous VirtualLock call, but it must specify a locked range" for this API to be successful. Note that the specified range is used to compute the range of pages covered by the unlock. A 2 byte unlock that straddles a page boundry ends up unlocking both of the pages covered by the range. Arguments: lpAddress - Supplies the base address of the region being unlocked. dwSize - Supplies the number of bytes being unlocked. Return Value: TRUE - The operation was was successful. FALSE - The operation failed. Extended error status is available using GetLastError. --*/ { NTSTATUS Status; PVOID BaseAddress; SIZE_T RegionSize; BOOL ReturnValue; ReturnValue = TRUE; BaseAddress = lpAddress; RegionSize = dwSize; Status = NtUnlockVirtualMemory( NtCurrentProcess(), &lpAddress, &RegionSize, MAP_PROCESS ); if ( !NT_SUCCESS(Status) ) { BaseSetLastNTError(Status); ReturnValue = FALSE; } return ReturnValue; } BOOL WINAPI FlushInstructionCache( HANDLE hProcess, LPCVOID lpBaseAddress, SIZE_T dwSize ) /*++ Routine Description: This function flushes the instruction cache for the specified process. Arguments: hProcess - Supplies a handle to the process in which the instruction cache is to be flushed. lpBaseAddress - Supplies an optional pointer to base of the region that is flushed. dwSize - Supplies the length of the region that is flushed if the base address is specified. Return Value: TRUE - The operation was was successful. FALSE - The operation failed. Extended error status is available using GetLastError. --*/ { NTSTATUS Status; BOOL ReturnValue = TRUE; Status = NtFlushInstructionCache( hProcess, (LPVOID)lpBaseAddress, dwSize ); if ( !NT_SUCCESS(Status) ) { BaseSetLastNTError(Status); ReturnValue = FALSE; } return ReturnValue; } BOOL WINAPI AllocateUserPhysicalPages( HANDLE hProcess, PULONG_PTR NumberOfPages, PULONG_PTR PageArray ) { NTSTATUS Status; Status = NtAllocateUserPhysicalPages( hProcess, NumberOfPages, PageArray); if (NT_SUCCESS( Status )) { return( TRUE ); } else { BaseSetLastNTError( Status ); return( FALSE ); } } BOOL WINAPI FreeUserPhysicalPages( HANDLE hProcess, PULONG_PTR NumberOfPages, PULONG_PTR PageArray ) { NTSTATUS Status; Status = NtFreeUserPhysicalPages( hProcess, NumberOfPages, PageArray); if (NT_SUCCESS( Status )) { return( TRUE ); } else { BaseSetLastNTError( Status ); return( FALSE ); } } BOOL WINAPI MapUserPhysicalPages( PVOID VirtualAddress, ULONG_PTR NumberOfPages, PULONG_PTR PageArray ) { NTSTATUS Status; Status = NtMapUserPhysicalPages( VirtualAddress, NumberOfPages, PageArray); if (NT_SUCCESS( Status )) { return( TRUE ); } else { BaseSetLastNTError( Status ); return( FALSE ); } } BOOL WINAPI MapUserPhysicalPagesScatter( PVOID *VirtualAddresses, ULONG_PTR NumberOfPages, PULONG_PTR PageArray ) { NTSTATUS Status; Status = NtMapUserPhysicalPagesScatter( VirtualAddresses, NumberOfPages, PageArray); if (NT_SUCCESS( Status )) { return( TRUE ); } else { BaseSetLastNTError( Status ); return( FALSE ); } } BOOL WINAPI GlobalMemoryStatusEx( LPMEMORYSTATUSEX lpBuffer ) { DWORD NumberOfPhysicalPages; SYSTEM_PERFORMANCE_INFORMATION PerfInfo; VM_COUNTERS VmCounters; QUOTA_LIMITS QuotaLimits; DWORDLONG AvailPageFile; DWORDLONG PhysicalMemory; NTSTATUS Status; DWORD Success; DWORDLONG address64; if (lpBuffer->dwLength != sizeof(*lpBuffer)) { SetLastError( ERROR_INVALID_PARAMETER ); return FALSE; } Status = NtQuerySystemInformation (SystemPerformanceInformation, &PerfInfo, sizeof(PerfInfo), NULL); if (!NT_SUCCESS (Status)) { BaseSetLastNTError (Status); return FALSE; } // // Capture the number of physical pages as it can change dynamically. // If it goes up or down in the middle of this routine, the results may // look strange (ie: available > total, etc), but it will quickly // right itself. // NumberOfPhysicalPages = USER_SHARED_DATA->NumberOfPhysicalPages; #if defined(BUILD_WOW6432) // // Convert the number of physical pages from the native system to // the emulation system. // NumberOfPhysicalPages = NumberOfPhysicalPages * (Wow64GetSystemNativePageSize() / BASE_SYSINFO.PageSize); #endif PhysicalMemory = (DWORDLONG)NumberOfPhysicalPages * BASE_SYSINFO.PageSize; // // Determine the memory load. < 100 available pages is 100 // Otherwise load is ((TotalPhys - AvailPhys) * 100) / TotalPhys // if (PerfInfo.AvailablePages < 100) { lpBuffer->dwMemoryLoad = 100; } else { lpBuffer->dwMemoryLoad = ((DWORD)(NumberOfPhysicalPages - PerfInfo.AvailablePages) * 100) / NumberOfPhysicalPages; } lpBuffer->ullTotalPhys = PhysicalMemory; PhysicalMemory = PerfInfo.AvailablePages; PhysicalMemory *= BASE_SYSINFO.PageSize; lpBuffer->ullAvailPhys = PhysicalMemory; // // Zero returned values in case the query process fails. // RtlZeroMemory (&QuotaLimits, sizeof (QUOTA_LIMITS)); RtlZeroMemory (&VmCounters, sizeof (VM_COUNTERS)); Status = NtQueryInformationProcess (NtCurrentProcess(), ProcessQuotaLimits, &QuotaLimits, sizeof(QUOTA_LIMITS), NULL ); if (!NT_SUCCESS (Status)) { BaseSetLastNTError (Status); return FALSE; } Status = NtQueryInformationProcess (NtCurrentProcess(), ProcessVmCounters, &VmCounters, sizeof(VM_COUNTERS), NULL ); if (!NT_SUCCESS (Status)) { BaseSetLastNTError (Status); return FALSE; } // // Determine the total page file space with respect to this process. // lpBuffer->ullTotalPageFile = PerfInfo.CommitLimit; if (QuotaLimits.PagefileLimit < PerfInfo.CommitLimit) { lpBuffer->ullTotalPageFile = QuotaLimits.PagefileLimit; } lpBuffer->ullTotalPageFile *= BASE_SYSINFO.PageSize; // // Determine remaining page file space with respect to this process. // AvailPageFile = PerfInfo.CommitLimit - PerfInfo.CommittedPages; lpBuffer->ullAvailPageFile = QuotaLimits.PagefileLimit - VmCounters.PagefileUsage; if ((ULONG)lpBuffer->ullAvailPageFile > (ULONG)AvailPageFile) { lpBuffer->ullAvailPageFile = AvailPageFile; } lpBuffer->ullAvailPageFile *= BASE_SYSINFO.PageSize; lpBuffer->ullTotalVirtual = (BASE_SYSINFO.MaximumUserModeAddress - BASE_SYSINFO.MinimumUserModeAddress) + 1; lpBuffer->ullAvailVirtual = lpBuffer->ullTotalVirtual - VmCounters.VirtualSize; lpBuffer->ullAvailExtendedVirtual = 0; return TRUE; } WINBASEAPI UINT WINAPI GetWriteWatch( DWORD dwFlags, PVOID lpBaseAddress, SIZE_T dwRegionSize, PVOID *addresses, ULONG_PTR *count, LPDWORD granularity ) { NTSTATUS Status; Status = NtGetWriteWatch ( NtCurrentProcess(), dwFlags, lpBaseAddress, dwRegionSize, addresses, count, granularity ); // // Note these return codes are taken straight from Win9x. // if (NT_SUCCESS( Status )) { return( 0 ); } else { BaseSetLastNTError( Status ); return (UINT)-1; } } WINBASEAPI UINT WINAPI ResetWriteWatch( LPVOID lpBaseAddress, SIZE_T dwRegionSize ) { NTSTATUS Status; Status = NtResetWriteWatch ( NtCurrentProcess(), lpBaseAddress, dwRegionSize ); // // Note these return codes are taken straight from Win9x. // if (NT_SUCCESS( Status )) { return( 0 ); } else { BaseSetLastNTError( Status ); return (UINT)-1; } } SIZE_T WINAPI GetLargePageMinimum ( VOID ) /*++ Routine Description: This function returns the size in bytes of the minimum large page size and address alignment that can be used with the VirtualAlloc MEM_LARGE_PAGES flag. Arguments: None. Return Value: The size in bytes of the minimum large page or zero if no large page is supported by the underlying hardware. --*/ { return (SIZE_T) USER_SHARED_DATA->LargePageMinimum; }