github.com/PandaGoAdmin/utils@v0.0.0-20211208134815-d5461603a00f/os_windows.go (about) 1 // +build windows 2 3 package kgo 4 5 import ( 6 "errors" 7 "fmt" 8 "github.com/StackExchange/wmi" 9 "golang.org/x/sys/windows" 10 "os" 11 "strings" 12 "syscall" 13 "time" 14 "unsafe" 15 ) 16 17 //内存状态扩展 18 type memoryStatusEx struct { 19 cbSize uint32 20 dwMemoryLoad uint32 21 ullTotalPhys uint64 // in bytes 22 ullAvailPhys uint64 23 ullTotalPageFile uint64 24 ullAvailPageFile uint64 25 ullTotalVirtual uint64 26 ullAvailVirtual uint64 27 ullAvailExtendedVirtual uint64 28 } 29 30 type fileTime struct { 31 DwLowDateTime uint32 32 DwHighDateTime uint32 33 } 34 35 type win32BIOS struct { 36 InstallDate *string 37 Manufacturer *string 38 Version *string 39 } 40 41 type win32Baseboard struct { 42 Manufacturer *string 43 SerialNumber *string 44 Tag *string 45 Version *string 46 Product *string 47 } 48 49 type win32Processor struct { 50 Manufacturer *string 51 Name *string 52 NumberOfLogicalProcessors uint32 53 NumberOfCores uint32 54 MaxClockSpeed uint32 55 L2CacheSize uint32 56 L3CacheSize uint32 57 } 58 59 type Win32_Process struct { 60 Name string 61 ExecutablePath *string 62 CommandLine *string 63 Priority uint32 64 CreationDate *time.Time 65 ProcessId uint32 66 ThreadCount uint32 67 Status *string 68 ReadOperationCount uint64 69 ReadTransferCount uint64 70 WriteOperationCount uint64 71 WriteTransferCount uint64 72 CSCreationClassName string 73 CSName string 74 Caption *string 75 CreationClassName string 76 Description *string 77 ExecutionState *uint16 78 HandleCount uint32 79 KernelModeTime uint64 80 MaximumWorkingSetSize *uint32 81 MinimumWorkingSetSize *uint32 82 OSCreationClassName string 83 OSName string 84 OtherOperationCount uint64 85 OtherTransferCount uint64 86 PageFaults uint32 87 PageFileUsage uint32 88 ParentProcessID uint32 89 PeakPageFileUsage uint32 90 PeakVirtualSize uint64 91 PeakWorkingSetSize uint32 92 PrivatePageCount uint64 93 TerminationDate *time.Time 94 UserModeTime uint64 95 WorkingSetSize uint64 96 } 97 98 var ( 99 kernel32 = windows.NewLazySystemDLL("kernel32.dll") 100 101 procGlobalMemoryStatusEx = kernel32.NewProc("GlobalMemoryStatusEx") 102 procGetSystemTimes = kernel32.NewProc("GetSystemTimes") 103 procGetDiskFreeSpaceExW = kernel32.NewProc("GetDiskFreeSpaceExW") 104 procGetTickCount64 = kernel32.NewProc("GetTickCount64") 105 procGetTickCount32 = kernel32.NewProc("GetTickCount") 106 ) 107 108 // getProcessByPid 根据pid获取进程列表. 109 func getProcessByPid(pid int) (res []Win32_Process) { 110 var dst []Win32_Process 111 query := fmt.Sprintf("WHERE ProcessId = %d", pid) 112 q := wmi.CreateQuery(&dst, query) 113 err := wmi.Query(q, &dst) 114 if err == nil && len(dst) > 0 { 115 res = dst 116 } 117 118 return 119 } 120 121 // getProcessPathByPid 根据PID获取进程的执行路径. 122 func getProcessPathByPid(pid int) (res string) { 123 ps := getProcessByPid(pid) 124 if len(ps) > 0 { 125 res = *ps[0].ExecutablePath 126 } 127 128 return 129 } 130 131 // getPidByPort 根据端口号获取监听的进程PID. 132 func getPidByPort(port int) (pid int) { 133 return 134 } 135 136 // HomeDir 获取当前用户的主目录. 137 func (ko *LkkOS) HomeDir() (string, error) { 138 // First prefer the HOME environmental variable 139 if home := os.Getenv("HOME"); home != "" { 140 return home, nil 141 } 142 143 // Prefer standard environment variable USERPROFILE 144 if home := os.Getenv("USERPROFILE"); home != "" { 145 return home, nil 146 } 147 148 drive := os.Getenv("HOMEDRIVE") 149 path := os.Getenv("HOMEPATH") 150 home := drive + path 151 if drive == "" || path == "" { 152 return "", errors.New("[HomeDir] HOMEDRIVE, HOMEPATH, or USERPROFILE are blank") 153 } 154 155 return home, nil 156 } 157 158 // MemoryUsage 获取内存使用率,单位字节. 159 // 参数 virtual(仅支持linux),是否取虚拟内存. 160 // used为已用, 161 // free为空闲, 162 // total为总数. 163 func (ko *LkkOS) MemoryUsage(virtual bool) (used, free, total uint64) { 164 var memInfo memoryStatusEx 165 memInfo.cbSize = uint32(unsafe.Sizeof(memInfo)) 166 mem, _, _ := procGlobalMemoryStatusEx.Call(uintptr(unsafe.Pointer(&memInfo))) 167 if mem == 0 { 168 return 169 } 170 171 total = memInfo.ullTotalPhys 172 free = memInfo.ullAvailPhys 173 used = memInfo.ullTotalPhys - memInfo.ullAvailPhys 174 175 return 176 } 177 178 // CpuUsage 获取CPU使用率(darwin系统必须使用cgo),单位jiffies(节拍数). 179 // user为用户态(用户进程)的运行时间, 180 // idle为空闲时间, 181 // total为累计时间. 182 func (ko *LkkOS) CpuUsage() (user, idle, total uint64) { 183 var lpIdleTime fileTime 184 var lpKernelTime fileTime 185 var lpUserTime fileTime 186 r, _, _ := procGetSystemTimes.Call( 187 uintptr(unsafe.Pointer(&lpIdleTime)), 188 uintptr(unsafe.Pointer(&lpKernelTime)), 189 uintptr(unsafe.Pointer(&lpUserTime))) 190 if r == 0 { 191 return 192 } 193 194 LOT := float64(0.0000001) 195 HIT := (LOT * 4294967296.0) 196 tmpIdle := ((HIT * float64(lpIdleTime.DwHighDateTime)) + (LOT * float64(lpIdleTime.DwLowDateTime))) 197 tmpUser := ((HIT * float64(lpUserTime.DwHighDateTime)) + (LOT * float64(lpUserTime.DwLowDateTime))) 198 tmpKernel := ((HIT * float64(lpKernelTime.DwHighDateTime)) + (LOT * float64(lpKernelTime.DwLowDateTime))) 199 //tmpSystem := (tmpKernel - tmpIdle) 200 201 user = uint64(tmpUser) 202 idle = uint64(tmpIdle) 203 total = user + idle + uint64(tmpKernel) 204 205 return 206 } 207 208 // DiskUsage 获取磁盘(目录)使用情况,单位字节.参数path为路径. 209 // used为已用, 210 // free为空闲, 211 // total为总数. 212 func (ko *LkkOS) DiskUsage(path string) (used, free, total uint64) { 213 lpFreeBytesAvailable := int64(0) 214 lpTotalNumberOfBytes := int64(0) 215 lpTotalNumberOfFreeBytes := int64(0) 216 diskret, _, _ := procGetDiskFreeSpaceExW.Call( 217 uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(path))), 218 uintptr(unsafe.Pointer(&lpFreeBytesAvailable)), 219 uintptr(unsafe.Pointer(&lpTotalNumberOfBytes)), 220 uintptr(unsafe.Pointer(&lpTotalNumberOfFreeBytes))) 221 if diskret == 0 { 222 return 223 } 224 total = uint64(lpTotalNumberOfBytes) 225 free = uint64(lpTotalNumberOfFreeBytes) 226 used = uint64(lpTotalNumberOfBytes - lpTotalNumberOfFreeBytes) 227 228 return 229 } 230 231 // Uptime 获取系统运行时间,秒. 232 func (ko *LkkOS) Uptime() (uint64, error) { 233 procGetTickCount := procGetTickCount64 234 err := procGetTickCount64.Find() 235 if err != nil { 236 // handle WinXP, but keep in mind that "the time will wrap around to zero if the system is run continuously for 49.7 days." from MSDN 237 procGetTickCount = procGetTickCount32 238 } 239 r1, _, lastErr := syscall.Syscall(procGetTickCount.Addr(), 0, 0, 0, 0) 240 if lastErr != 0 { 241 return 0, lastErr 242 } 243 return uint64((time.Duration(r1) * time.Millisecond).Seconds()), nil 244 } 245 246 // GetBiosInfo 获取BIOS信息. 247 // 注意:Mac机器没有BIOS信息,它使用EFI. 248 func (ko *LkkOS) GetBiosInfo() *BiosInfo { 249 res := &BiosInfo{ 250 Vendor: "", 251 Version: "", 252 Date: "", 253 } 254 255 // Getting data from WMI 256 var win32BIOSDescriptions []win32BIOS 257 if err := wmi.Query("SELECT InstallDate, Manufacturer, Version FROM CIM_BIOSElement", &win32BIOSDescriptions); err != nil { 258 return res 259 } 260 if len(win32BIOSDescriptions) > 0 { 261 res.Vendor = *win32BIOSDescriptions[0].Manufacturer 262 res.Version = *win32BIOSDescriptions[0].Version 263 res.Date = *win32BIOSDescriptions[0].InstallDate 264 } 265 266 return res 267 } 268 269 // GetBoardInfo 获取Board信息. 270 func (ko *LkkOS) GetBoardInfo() *BoardInfo { 271 res := &BoardInfo{ 272 Name: "", 273 Vendor: "", 274 Version: "", 275 Serial: "", 276 AssetTag: "", 277 } 278 279 // Getting data from WMI 280 var win32BaseboardDescriptions []win32Baseboard 281 if err := wmi.Query("SELECT Manufacturer, SerialNumber, Tag, Version, Product FROM Win32_BaseBoard", &win32BaseboardDescriptions); err != nil { 282 return res 283 } 284 if len(win32BaseboardDescriptions) > 0 { 285 res.Name = *win32BaseboardDescriptions[0].Product 286 res.Vendor = *win32BaseboardDescriptions[0].Manufacturer 287 res.Version = *win32BaseboardDescriptions[0].Version 288 res.Serial = *win32BaseboardDescriptions[0].SerialNumber 289 res.AssetTag = *win32BaseboardDescriptions[0].Tag 290 } 291 292 return res 293 } 294 295 // GetCpuInfo 获取CPU信息. 296 func (ko *LkkOS) GetCpuInfo() *CpuInfo { 297 var res = &CpuInfo{ 298 Vendor: "", 299 Model: "", 300 Speed: "", 301 Cache: 0, 302 Cpus: 0, 303 Cores: 0, 304 Threads: 0, 305 } 306 307 // Getting info from WMI 308 var win32descriptions []win32Processor 309 if err := wmi.Query("SELECT Manufacturer, Name, NumberOfLogicalProcessors, NumberOfCores, MaxClockSpeed, L2CacheSize, L3CacheSize FROM Win32_Processor", &win32descriptions); err != nil { 310 return res 311 } 312 313 var cores, threads uint 314 for _, description := range win32descriptions { 315 if res.Vendor == "" { 316 res.Vendor = *description.Manufacturer 317 } 318 if res.Model == "" { 319 res.Model = *description.Name 320 } 321 if res.Speed == "" { 322 res.Speed = toStr(description.MaxClockSpeed) 323 } 324 if res.Cache == 0 { 325 res.Cache = uint(description.L2CacheSize + description.L3CacheSize) 326 } 327 328 cores += uint(description.NumberOfCores) 329 threads += uint(description.NumberOfLogicalProcessors) 330 } 331 332 res.Cpus = uint(len(win32descriptions)) 333 res.Cores = cores 334 res.Threads = threads 335 336 return res 337 } 338 339 // IsProcessExists 进程是否存在. 340 func (ko *LkkOS) IsProcessExists(pid int) (res bool) { 341 if pid <= 0 { 342 return 343 } else if pid%4 != 0 { 344 // OpenProcess will succeed even on non-existing pid here https://devblogs.microsoft.com/oldnewthing/20080606-00/?p=22043 345 // so we list every pid just to be sure and be future-proof 346 ps := getProcessByPid(pid) 347 if len(ps) > 0 && ps[0].ProcessId > 0 { 348 res = true 349 return 350 } 351 } else { 352 var still_active uint32 = 259 // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getexitcodeprocess 353 h, err := windows.OpenProcess(windows.PROCESS_QUERY_LIMITED_INFORMATION, false, uint32(pid)) 354 if err == windows.ERROR_ACCESS_DENIED { 355 return true 356 } 357 if err == windows.ERROR_INVALID_PARAMETER { 358 return false 359 } 360 if err != nil { 361 return false 362 } 363 364 defer func() { 365 _ = syscall.CloseHandle(syscall.Handle(h)) 366 }() 367 var exitCode uint32 368 _ = windows.GetExitCodeProcess(h, &exitCode) 369 res = exitCode == still_active 370 } 371 372 return 373 } 374 375 // GetPidByPort 根据端口号获取监听的进程PID. 376 // linux可能要求root权限; 377 // darwin依赖lsof; 378 // windows依赖netstat. 379 func (ko *LkkOS) GetPidByPort(port int) (pid int) { 380 command := fmt.Sprintf("cmd /C netstat -ano | findstr %d", port) 381 _, out, _ := ko.System(command) 382 lines := strings.Split(string(out), "\r\n") 383 for _, line := range lines { 384 fields := strings.Fields(line) 385 if len(fields) == 5 && isNumeric(fields[4]) { 386 p := toInt(fields[4]) 387 if p > 0 { 388 pid = p 389 break 390 } 391 } 392 } 393 394 return 395 }