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  }