github.com/isyscore/isc-gobase@v1.5.3-0.20231218061332-cbc7451899e9/system/mem/mem_windows.go (about) 1 //go:build windows 2 3 package mem 4 5 import ( 6 "context" 7 "github.com/isyscore/isc-gobase/system/common" 8 "golang.org/x/sys/windows" 9 "sync" 10 "syscall" 11 "unsafe" 12 ) 13 14 var ( 15 procEnumPageFilesW = common.ModPsapi.NewProc("EnumPageFilesW") 16 procGetNativeSystemInfo = common.Modkernel32.NewProc("GetNativeSystemInfo") 17 procGetPerformanceInfo = common.ModPsapi.NewProc("GetPerformanceInfo") 18 procGlobalMemoryStatusEx = common.Modkernel32.NewProc("GlobalMemoryStatusEx") 19 ) 20 21 type memoryStatusEx struct { 22 cbSize uint32 23 dwMemoryLoad uint32 24 ullTotalPhys uint64 // in bytes 25 ullAvailPhys uint64 26 ullTotalPageFile uint64 27 ullAvailPageFile uint64 28 ullTotalVirtual uint64 29 ullAvailVirtual uint64 30 ullAvailExtendedVirtual uint64 31 } 32 33 func VirtualMemory() (*VirtualMemoryStat, error) { 34 return VirtualMemoryWithContext(context.Background()) 35 } 36 37 func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) { 38 var memInfo memoryStatusEx 39 memInfo.cbSize = uint32(unsafe.Sizeof(memInfo)) 40 mem, _, _ := procGlobalMemoryStatusEx.Call(uintptr(unsafe.Pointer(&memInfo))) 41 if mem == 0 { 42 return nil, windows.GetLastError() 43 } 44 45 ret := &VirtualMemoryStat{ 46 Total: memInfo.ullTotalPhys, 47 Available: memInfo.ullAvailPhys, 48 Free: memInfo.ullAvailPhys, 49 UsedPercent: float64(memInfo.dwMemoryLoad), 50 } 51 52 ret.Used = ret.Total - ret.Available 53 return ret, nil 54 } 55 56 type performanceInformation struct { 57 cb uint32 58 commitTotal uint64 59 commitLimit uint64 60 commitPeak uint64 61 physicalTotal uint64 62 physicalAvailable uint64 63 systemCache uint64 64 kernelTotal uint64 65 kernelPaged uint64 66 kernelNonpaged uint64 67 pageSize uint64 68 handleCount uint32 69 processCount uint32 70 threadCount uint32 71 } 72 73 func SwapMemory() (*SwapMemoryStat, error) { 74 return SwapMemoryWithContext(context.Background()) 75 } 76 77 func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) { 78 var perfInfo performanceInformation 79 perfInfo.cb = uint32(unsafe.Sizeof(perfInfo)) 80 mem, _, _ := procGetPerformanceInfo.Call(uintptr(unsafe.Pointer(&perfInfo)), uintptr(perfInfo.cb)) 81 if mem == 0 { 82 return nil, windows.GetLastError() 83 } 84 tot := perfInfo.commitLimit * perfInfo.pageSize 85 used := perfInfo.commitTotal * perfInfo.pageSize 86 free := tot - used 87 var usedPercent float64 88 if tot == 0 { 89 usedPercent = 0 90 } else { 91 usedPercent = float64(used) / float64(tot) * 100 92 } 93 ret := &SwapMemoryStat{ 94 Total: tot, 95 Used: used, 96 Free: free, 97 UsedPercent: usedPercent, 98 } 99 100 return ret, nil 101 } 102 103 var ( 104 pageSize uint64 105 pageSizeOnce sync.Once 106 ) 107 108 type systemInfo struct { 109 wProcessorArchitecture uint16 110 wReserved uint16 111 dwPageSize uint32 112 lpMinimumApplicationAddress uintptr 113 lpMaximumApplicationAddress uintptr 114 dwActiveProcessorMask uintptr 115 dwNumberOfProcessors uint32 116 dwProcessorType uint32 117 dwAllocationGranularity uint32 118 wProcessorLevel uint16 119 wProcessorRevision uint16 120 } 121 122 // system type as defined in https://docs.microsoft.com/en-us/windows/win32/api/psapi/ns-psapi-enum_page_file_information 123 type enumPageFileInformation struct { 124 cb uint32 125 reserved uint32 126 totalSize uint64 127 totalInUse uint64 128 peakUsage uint64 129 } 130 131 func SwapDevices() ([]*SwapDevice, error) { 132 return SwapDevicesWithContext(context.Background()) 133 } 134 135 func SwapDevicesWithContext(ctx context.Context) ([]*SwapDevice, error) { 136 pageSizeOnce.Do(func() { 137 var sysInfo systemInfo 138 _, _, _ = procGetNativeSystemInfo.Call(uintptr(unsafe.Pointer(&sysInfo))) 139 pageSize = uint64(sysInfo.dwPageSize) 140 }) 141 142 // the following system call invokes the supplied callback function once for each page file before returning 143 // see https://docs.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-enumpagefilesw 144 var swapDevices []*SwapDevice 145 result, _, _ := procEnumPageFilesW.Call(windows.NewCallback(pEnumPageFileCallbackW), uintptr(unsafe.Pointer(&swapDevices))) 146 if result == 0 { 147 return nil, windows.GetLastError() 148 } 149 150 return swapDevices, nil 151 } 152 153 // system callback as defined in https://docs.microsoft.com/en-us/windows/win32/api/psapi/nc-psapi-penum_page_file_callbackw 154 func pEnumPageFileCallbackW(swapDevices *[]*SwapDevice, enumPageFileInfo *enumPageFileInformation, lpFilenamePtr *[syscall.MAX_LONG_PATH]uint16) *bool { 155 *swapDevices = append(*swapDevices, &SwapDevice{ 156 Name: syscall.UTF16ToString((*lpFilenamePtr)[:]), 157 UsedBytes: enumPageFileInfo.totalInUse * pageSize, 158 FreeBytes: (enumPageFileInfo.totalSize - enumPageFileInfo.totalInUse) * pageSize, 159 }) 160 161 // return true to continue enumerating page files 162 ret := true 163 return &ret 164 }