@daimazun/hardware-info 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +907 -0
- package/bin/OpenHardwareMonitorLib.dll +0 -0
- package/index.js +192 -0
- package/lib/allhardware.js +35 -0
- package/lib/backend-usage.js +103 -0
- package/lib/battery.js +76 -0
- package/lib/disk-watcher.js +156 -0
- package/lib/disk.js +77 -0
- package/lib/gpu.js +70 -0
- package/lib/hardware-service.js +284 -0
- package/lib/memory.js +59 -0
- package/lib/monitor.js +176 -0
- package/lib/network.js +75 -0
- package/lib/ohm-daemon.js +208 -0
- package/lib/ohm.js +41 -0
- package/lib/perfctr.js +43 -0
- package/lib/process-icon.js +76 -0
- package/lib/process-ops.js +218 -0
- package/lib/processes.js +232 -0
- package/lib/ps.js +93 -0
- package/lib/public-ip.js +124 -0
- package/lib/services.js +43 -0
- package/lib/sysinfo-daemon.js +188 -0
- package/lib/system.js +77 -0
- package/lib/usb.js +49 -0
- package/lib/win32-procs.js +241 -0
- package/package.json +50 -0
- package/scripts/get-all-hardware.ps1 +98 -0
- package/scripts/get-lhm-temp.ps1 +119 -0
- package/scripts/ohm-daemon.ps1 +141 -0
- package/scripts/sysinfo-daemon.ps1 +386 -0
- package/test/check-bugs.js +182 -0
- package/test/public/gj.pay.ali.jpg +0 -0
- package/test/public/gj.pay.wx.jpg +0 -0
- package/test/public/index.html +996 -0
- package/test/public/zdl.pay.ali.jpg +0 -0
- package/test/public/zdl.pay.wx.jpg +0 -0
- package/test/scan-encoding.js +77 -0
- package/test/server.js +255 -0
- package/test/test-all.js +180 -0
- package/test/test-daemon.js +51 -0
- package/test/test-kill-name.js +30 -0
- package/test/test-monitor.js +78 -0
- package/test/test-new-features.js +93 -0
- package/test/test-service.js +91 -0
- package/test/test-sysinfo-daemon.js +95 -0
- package/test/test.js +63 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
param(
|
|
2
|
+
[string]$DllPath = ""
|
|
3
|
+
)
|
|
4
|
+
|
|
5
|
+
# UTF-8 输出,解决中文乱码
|
|
6
|
+
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
|
7
|
+
$OutputEncoding = [System.Text.Encoding]::UTF8
|
|
8
|
+
chcp 65001 > $null
|
|
9
|
+
|
|
10
|
+
$ErrorActionPreference = "Stop"
|
|
11
|
+
|
|
12
|
+
if (-not $DllPath) {
|
|
13
|
+
$DllPath = Join-Path $PSScriptRoot "..\bin\OpenHardwareMonitorLib.dll"
|
|
14
|
+
}
|
|
15
|
+
$DllPath = (Resolve-Path $DllPath).Path
|
|
16
|
+
|
|
17
|
+
if (-not (Test-Path $DllPath)) {
|
|
18
|
+
Write-Error "DLL not found: $DllPath"
|
|
19
|
+
exit 1
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
Unblock-File -LiteralPath $DllPath -ErrorAction SilentlyContinue
|
|
24
|
+
Add-Type -LiteralPath $DllPath
|
|
25
|
+
} catch {
|
|
26
|
+
Write-Error "Failed to load DLL: $_"
|
|
27
|
+
exit 1
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
$monitor = New-Object OpenHardwareMonitor.Hardware.Computer
|
|
31
|
+
$monitor.CPUEnabled = $true
|
|
32
|
+
$monitor.MainboardEnabled = $false
|
|
33
|
+
$monitor.GPUEnabled = $false
|
|
34
|
+
$monitor.HDDEnabled = $false
|
|
35
|
+
$monitor.RAMEnabled = $false
|
|
36
|
+
$monitor.FanControllerEnabled = $false
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
$monitor.Open()
|
|
40
|
+
} catch {
|
|
41
|
+
Write-Error "Failed to open monitor (needs admin?): $_"
|
|
42
|
+
exit 1
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function Get-CpuData {
|
|
46
|
+
$script:cpuName = ""
|
|
47
|
+
$script:allSensors = @()
|
|
48
|
+
|
|
49
|
+
function Walk-Hardware {
|
|
50
|
+
param($hw)
|
|
51
|
+
$hw.Update()
|
|
52
|
+
foreach ($sensor in $hw.Sensors) {
|
|
53
|
+
$val = $sensor.Value
|
|
54
|
+
if ($null -ne $val) {
|
|
55
|
+
$script:allSensors += [PSCustomObject]@{
|
|
56
|
+
name = $sensor.Name
|
|
57
|
+
type = $sensor.SensorType.ToString()
|
|
58
|
+
value = [math]::Round([double]$val, 2)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
foreach ($sub in $hw.SubHardware) {
|
|
63
|
+
Walk-Hardware $sub
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
foreach ($hw in $monitor.Hardware) {
|
|
68
|
+
if ($hw.HardwareType -eq [OpenHardwareMonitor.Hardware.HardwareType]::CPU) {
|
|
69
|
+
$script:cpuName = $hw.Name
|
|
70
|
+
Walk-Hardware $hw
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if ($script:allSensors.Count -eq 0) {
|
|
75
|
+
return $null
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function Get-ByType {
|
|
79
|
+
param($type)
|
|
80
|
+
return @($script:allSensors | Where-Object { $_.type -eq $type } | ForEach-Object {
|
|
81
|
+
[PSCustomObject]@{ name = $_.name; value = $_.value }
|
|
82
|
+
})
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
$tempSensors = Get-ByType "Temperature"
|
|
86
|
+
$loadSensors = Get-ByType "Load"
|
|
87
|
+
$clockSensors = Get-ByType "Clock"
|
|
88
|
+
$powerSensors = Get-ByType "Power"
|
|
89
|
+
|
|
90
|
+
# 没有温度传感器说明驱动没加载成功(大概率非管理员)
|
|
91
|
+
if ($tempSensors.Count -eq 0) {
|
|
92
|
+
return [PSCustomObject]@{ error = "no temperature sensors (likely no admin privileges, WinRing0 driver not loaded)" }
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
$coreTemps = @($tempSensors | Where-Object { $_.name -match "Core" })
|
|
96
|
+
$packageTemp = ($tempSensors | Where-Object { $_.name -match "Package" } | Select-Object -First 1).value
|
|
97
|
+
$maxCoreTemp = if ($coreTemps.Count -gt 0) { ($coreTemps | Measure-Object -Property value -Maximum).Maximum } else { $null }
|
|
98
|
+
|
|
99
|
+
return [PSCustomObject]@{
|
|
100
|
+
source = "openhardwaremonitor_daemon"
|
|
101
|
+
isCoreTemp = $true
|
|
102
|
+
cpuName = $script:cpuName
|
|
103
|
+
temperature = [PSCustomObject]@{
|
|
104
|
+
cores = $coreTemps
|
|
105
|
+
package = $packageTemp
|
|
106
|
+
maxCore = $maxCoreTemp
|
|
107
|
+
}
|
|
108
|
+
load = $loadSensors
|
|
109
|
+
clock = $clockSensors
|
|
110
|
+
power = $powerSensors
|
|
111
|
+
timestamp = (Get-Date).ToString("o")
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
# 发送就绪信号,让 Node.js 知道进程已启动并完成驱动加载
|
|
116
|
+
Write-Output "READY"
|
|
117
|
+
|
|
118
|
+
# 主循环:等待 stdin 指令
|
|
119
|
+
while ($true) {
|
|
120
|
+
$line = [Console]::In.ReadLine()
|
|
121
|
+
if ($null -eq $line) { break }
|
|
122
|
+
$line = $line.Trim()
|
|
123
|
+
if ($line -eq "exit" -or $line -eq "quit") {
|
|
124
|
+
break
|
|
125
|
+
}
|
|
126
|
+
if ($line -eq "get" -or $line -eq "") {
|
|
127
|
+
try {
|
|
128
|
+
$data = Get-CpuData
|
|
129
|
+
if ($null -eq $data) {
|
|
130
|
+
Write-Output '{"error":"no sensors"}'
|
|
131
|
+
} else {
|
|
132
|
+
Write-Output ($data | ConvertTo-Json -Depth 6 -Compress)
|
|
133
|
+
}
|
|
134
|
+
} catch {
|
|
135
|
+
$errMsg = $_.Exception.Message -replace '"', '\"'
|
|
136
|
+
Write-Output "{`"error`":`"$errMsg`"}"
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
$monitor.Close()
|
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
# sysinfo-daemon.ps1 — 统一硬件信息常驻进程
|
|
2
|
+
# 通过 stdin 接收指令,stdout 输出 JSON,支持高频低延迟查询
|
|
3
|
+
|
|
4
|
+
param(
|
|
5
|
+
[int]$SystemCacheTtl = 30,
|
|
6
|
+
[int]$NetworkCacheTtl = 30
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
# UTF-8 输出
|
|
10
|
+
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
|
11
|
+
$OutputEncoding = [System.Text.Encoding]::UTF8
|
|
12
|
+
chcp 65001 > $null
|
|
13
|
+
|
|
14
|
+
$ErrorActionPreference = "Stop"
|
|
15
|
+
|
|
16
|
+
# ========== 缓存(变化不频繁的数据,启动时预加载)==========
|
|
17
|
+
$script:systemCache = $null
|
|
18
|
+
$script:networkAdapterCache = $null
|
|
19
|
+
$script:gpuCache = $null
|
|
20
|
+
$script:usbCache = $null
|
|
21
|
+
$script:cacheTime = @{}
|
|
22
|
+
|
|
23
|
+
function Get-CachedOrUpdate {
|
|
24
|
+
param($key, $ttlSeconds, $fetchBlock)
|
|
25
|
+
$now = Get-Date
|
|
26
|
+
if (-not $script:cacheTime.ContainsKey($key) -or ($now - $script:cacheTime[$key]).TotalSeconds -gt $ttlSeconds) {
|
|
27
|
+
$script:cacheTime[$key] = $now
|
|
28
|
+
return & $fetchBlock
|
|
29
|
+
}
|
|
30
|
+
return $null
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
# ========== 各模块查询函数 ==========
|
|
34
|
+
|
|
35
|
+
function Get-SystemInfo {
|
|
36
|
+
# 静态信息缓存 30 秒(电源计划可能变化),只更新 uptime 和 currentTime
|
|
37
|
+
$needRefresh = ($null -eq $script:systemCache) -or (-not $script:cacheTime.ContainsKey('system')) -or ((Get-Date) - $script:cacheTime['system']).TotalSeconds -gt $SystemCacheTtl
|
|
38
|
+
if ($needRefresh) {
|
|
39
|
+
$script:cacheTime['system'] = Get-Date
|
|
40
|
+
$os = Get-CimInstance Win32_OperatingSystem
|
|
41
|
+
$cs = Get-CimInstance Win32_ComputerSystem
|
|
42
|
+
$bios = Get-CimInstance Win32_BIOS
|
|
43
|
+
$board = Get-CimInstance Win32_BaseBoard
|
|
44
|
+
$cpu = Get-CimInstance Win32_Processor | Select-Object -First 1
|
|
45
|
+
try {
|
|
46
|
+
$powerPlan = (Get-CimInstance -Namespace "root\cimv2\power" -ClassName Win32_PowerPlan | Where-Object { $_.IsActive -eq $true } | Select-Object -First 1).ElementName
|
|
47
|
+
} catch { $powerPlan = $null }
|
|
48
|
+
$script:systemCache = [PSCustomObject]@{
|
|
49
|
+
os = [PSCustomObject]@{
|
|
50
|
+
caption = $os.Caption; version = $os.Version; buildNumber = $os.BuildNumber
|
|
51
|
+
architecture = $os.OSArchitecture; installDate = $os.InstallDate.ToString('o')
|
|
52
|
+
lastBootUpTime = $os.LastBootUpTime.ToString('o'); systemDrive = $os.SystemDrive
|
|
53
|
+
}
|
|
54
|
+
computer = [PSCustomObject]@{
|
|
55
|
+
manufacturer = $cs.Manufacturer; model = $cs.Model; name = $cs.Name
|
|
56
|
+
domain = $cs.Domain; totalPhysicalMemoryGB = [math]::Round($cs.TotalPhysicalMemory / 1GB, 2)
|
|
57
|
+
numberOfLogicalProcessors = $cs.NumberOfLogicalProcessors; systemType = $cs.SystemType
|
|
58
|
+
}
|
|
59
|
+
bios = [PSCustomObject]@{
|
|
60
|
+
manufacturer = $bios.Manufacturer; version = $bios.SMBIOSBIOSVersion
|
|
61
|
+
releaseDate = if ($bios.ReleaseDate) { $bios.ReleaseDate.ToString('o') } else { $null }
|
|
62
|
+
serialNumber = $bios.SerialNumber
|
|
63
|
+
}
|
|
64
|
+
motherboard = [PSCustomObject]@{
|
|
65
|
+
manufacturer = $board.Manufacturer; product = $board.Product; version = $board.Version; serialNumber = $board.SerialNumber
|
|
66
|
+
}
|
|
67
|
+
cpu = [PSCustomObject]@{
|
|
68
|
+
name = $cpu.Name; manufacturer = $cpu.Manufacturer
|
|
69
|
+
numberOfCores = $cpu.NumberOfCores; numberOfLogicalProcessors = $cpu.NumberOfLogicalProcessors
|
|
70
|
+
maxClockSpeedMHz = $cpu.MaxClockSpeed; l2CacheKB = $cpu.L2CacheSize; l3CacheKB = $cpu.L3CacheSize
|
|
71
|
+
socketDesignation = $cpu.SocketDesignation; virtualizationEnabled = $cpu.VirtualizationFirmwareEnabled
|
|
72
|
+
}
|
|
73
|
+
powerPlan = $powerPlan; timezone = (Get-TimeZone).Id
|
|
74
|
+
bootTime = $os.LastBootUpTime
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
$c = $script:systemCache
|
|
78
|
+
$uptime = (Get-Date) - $c.bootTime
|
|
79
|
+
return [PSCustomObject]@{
|
|
80
|
+
os = $c.os; computer = $c.computer; bios = $c.bios; motherboard = $c.motherboard
|
|
81
|
+
cpu = $c.cpu; powerPlan = $c.powerPlan; timezone = $c.timezone
|
|
82
|
+
currentTime = (Get-Date).ToString('o')
|
|
83
|
+
uptimeSeconds = [math]::Round($uptime.TotalSeconds); uptimeDays = [math]::Round($uptime.TotalDays, 2)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function Get-MemoryInfo {
|
|
88
|
+
$sticks = Get-CimInstance Win32_PhysicalMemory
|
|
89
|
+
$array = Get-CimInstance Win32_PhysicalMemoryArray | Select-Object -First 1
|
|
90
|
+
$os = Get-CimInstance Win32_OperatingSystem
|
|
91
|
+
$stickList = @()
|
|
92
|
+
foreach ($s in $sticks) {
|
|
93
|
+
$stickList += [PSCustomObject]@{
|
|
94
|
+
deviceLocator = $s.DeviceLocator; bankLabel = $s.BankLabel
|
|
95
|
+
capacityGB = [math]::Round($s.Capacity / 1GB, 2); speedMHz = $s.Speed
|
|
96
|
+
configuredSpeedMHz = $s.ConfiguredClockSpeed; manufacturer = $s.Manufacturer
|
|
97
|
+
partNumber = $s.PartNumber.Trim(); serialNumber = $s.SerialNumber; dataWidth = $s.DataWidth
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
$totalPhysicalGB = [math]::Round($os.TotalVisibleMemorySize / 1MB, 2)
|
|
101
|
+
$freePhysicalGB = [math]::Round($os.FreePhysicalMemory / 1MB, 2)
|
|
102
|
+
[PSCustomObject]@{
|
|
103
|
+
totalPhysicalGB = $totalPhysicalGB; usedPhysicalGB = [math]::Round($totalPhysicalGB - $freePhysicalGB, 2)
|
|
104
|
+
freePhysicalGB = $freePhysicalGB; usedPercent = [math]::Round((($totalPhysicalGB - $freePhysicalGB) / $totalPhysicalGB) * 100, 2)
|
|
105
|
+
totalVirtualGB = [math]::Round($os.TotalVirtualMemorySize / 1MB, 2); freeVirtualGB = [math]::Round($os.FreeVirtualMemory / 1MB, 2)
|
|
106
|
+
sticks = $stickList; stickCount = $stickList.Count
|
|
107
|
+
array = if ($array) { [PSCustomObject]@{ maxCapacityGB = [math]::Round($array.MaxCapacity / 1MB, 2); memoryDevices = $array.MemoryDevices } } else { $null }
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function Get-DiskInfo {
|
|
112
|
+
$disks = Get-CimInstance Win32_DiskDrive
|
|
113
|
+
$logical = Get-CimInstance Win32_LogicalDisk -Filter "DriveType=3"
|
|
114
|
+
$diskPerf = Get-CimInstance Win32_PerfFormattedData_PerfDisk_PhysicalDisk | Where-Object { $_.Name -ne '_Total' }
|
|
115
|
+
$diskList = @()
|
|
116
|
+
foreach ($d in $disks) {
|
|
117
|
+
$perf = $diskPerf | Where-Object { $_.Name -like "$($d.Index) *" } | Select-Object -First 1
|
|
118
|
+
# 关联物理磁盘 → 分区 → 逻辑盘符
|
|
119
|
+
$driveLetters = @()
|
|
120
|
+
try {
|
|
121
|
+
$parts = Get-CimAssociatedInstance -InputObject $d -ResultClassName Win32_DiskPartition -ErrorAction SilentlyContinue
|
|
122
|
+
foreach ($p in $parts) {
|
|
123
|
+
$logs = Get-CimAssociatedInstance -InputObject $p -ResultClassName Win32_LogicalDisk -ErrorAction SilentlyContinue
|
|
124
|
+
foreach ($l in $logs) { if ($l.DeviceID) { $driveLetters += $l.DeviceID } }
|
|
125
|
+
}
|
|
126
|
+
} catch { }
|
|
127
|
+
$diskList += [PSCustomObject]@{
|
|
128
|
+
index = $d.Index; model = $d.Model; interfaceType = $d.InterfaceType; mediaType = $d.MediaType
|
|
129
|
+
serialNumber = if ($d.SerialNumber) { $d.SerialNumber.Trim() } else { $null }
|
|
130
|
+
firmwareRevision = $d.FirmwareRevision; sizeGB = [math]::Round($d.Size / 1GB, 2)
|
|
131
|
+
drives = $driveLetters
|
|
132
|
+
readBytesPerSec = if ($perf) { $perf.DiskReadBytesPerSec } else { $null }
|
|
133
|
+
writeBytesPerSec = if ($perf) { $perf.DiskWriteBytesPerSec } else { $null }
|
|
134
|
+
percentActive = if ($perf) { $perf.PercentDiskTime } else { $null }
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
$partitionList = @()
|
|
138
|
+
foreach ($ld in $logical) {
|
|
139
|
+
$sizeGB = [math]::Round($ld.Size / 1GB, 2); $freeGB = [math]::Round($ld.FreeSpace / 1GB, 2)
|
|
140
|
+
$partitionList += [PSCustomObject]@{
|
|
141
|
+
drive = $ld.DeviceID; volumeName = $ld.VolumeName; fileSystem = $ld.FileSystem
|
|
142
|
+
sizeGB = $sizeGB; usedGB = [math]::Round($sizeGB - $freeGB, 2); freeGB = $freeGB
|
|
143
|
+
usedPercent = if ($sizeGB -gt 0) { [math]::Round((($sizeGB - $freeGB) / $sizeGB) * 100, 2) } else { 0 }
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
[PSCustomObject]@{ physicalDisks = $diskList; logicalDrives = $partitionList; diskCount = $diskList.Count; totalSizeGB = [math]::Round(($diskList | Measure-Object -Property sizeGB -Sum).Sum, 2) }
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function Get-NetworkInfo {
|
|
150
|
+
param($includeWifi = $false)
|
|
151
|
+
# 网卡配置缓存,每次只更新性能计数器(网速)
|
|
152
|
+
$needRefresh = (-not $script:networkAdapterCache) -or (-not $script:cacheTime.ContainsKey('network')) -or ((Get-Date) - $script:cacheTime['network']).TotalSeconds -gt $NetworkCacheTtl
|
|
153
|
+
if ($needRefresh) {
|
|
154
|
+
$script:cacheTime['network'] = Get-Date
|
|
155
|
+
$adapters = Get-CimInstance Win32_NetworkAdapter | Where-Object { $_.PhysicalAdapter -eq $true -or $_.NetConnectionID }
|
|
156
|
+
$configs = Get-CimInstance Win32_NetworkAdapterConfiguration | Where-Object { $_.IPEnabled }
|
|
157
|
+
$cached = @()
|
|
158
|
+
foreach ($a in $adapters) {
|
|
159
|
+
$cfg = $configs | Where-Object { $_.Index -eq $a.Index } | Select-Object -First 1
|
|
160
|
+
$counterName = $a.Name -replace '\(', '[' -replace '\)', ']'
|
|
161
|
+
$cached += [PSCustomObject]@{
|
|
162
|
+
index = $a.Index; name = $a.Name; connectionName = $a.NetConnectionID; macAddress = $a.MACAddress
|
|
163
|
+
speedMbps = if ($a.Speed) { [math]::Round($a.Speed / 1MB, 0) } else { $null }
|
|
164
|
+
status = $a.NetConnectionStatus; isPhysical = $a.PhysicalAdapter; counterName = $counterName
|
|
165
|
+
ipAddresses = if ($cfg) { $cfg.IPAddress } else { @() }
|
|
166
|
+
defaultIpGateway = if ($cfg) { $cfg.DefaultIPGateway } else { @() }
|
|
167
|
+
dnsServers = if ($cfg) { $cfg.DNSServerSearchOrder } else { @() }
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
$script:networkAdapterCache = $cached
|
|
171
|
+
}
|
|
172
|
+
# 每次只查性能计数器(网速)
|
|
173
|
+
$ifStats = Get-CimInstance Win32_PerfFormattedData_Tcpip_NetworkInterface
|
|
174
|
+
$wifi = $null
|
|
175
|
+
if ($includeWifi) {
|
|
176
|
+
try {
|
|
177
|
+
$wlan = netsh wlan show interfaces 2>$null
|
|
178
|
+
if ($wlan -match 'SSID') {
|
|
179
|
+
$ssid = ($wlan | Select-String 'SSID' | Select-Object -First 1).ToString() -replace '.*:\s*', ''
|
|
180
|
+
$signal = ($wlan | Select-String 'Signal' | Select-Object -First 1).ToString() -replace '.*:\s*', '' -replace '%', ''
|
|
181
|
+
$wifi = [PSCustomObject]@{ ssId = $ssid.Trim(); signalPercent = if ($signal) { [int]$signal.Trim() } else { $null } }
|
|
182
|
+
}
|
|
183
|
+
} catch {}
|
|
184
|
+
}
|
|
185
|
+
$adapterList = @()
|
|
186
|
+
foreach ($c in $script:networkAdapterCache) {
|
|
187
|
+
$stat = $ifStats | Where-Object { $_.Name -eq $c.connectionName -or $_.Name -eq $c.counterName } | Select-Object -First 1
|
|
188
|
+
$adapterList += [PSCustomObject]@{
|
|
189
|
+
name = $c.name; connectionName = $c.connectionName; macAddress = $c.macAddress
|
|
190
|
+
speedMbps = $c.speedMbps; status = $c.status; isPhysical = $c.isPhysical
|
|
191
|
+
ipAddresses = $c.ipAddresses; defaultIpGateway = $c.defaultIpGateway; dnsServers = $c.dnsServers
|
|
192
|
+
bytesReceivedPerSec = if ($stat) { $stat.BytesReceivedPerSec } else { $null }
|
|
193
|
+
bytesSentPerSec = if ($stat) { $stat.BytesSentPerSec } else { $null }
|
|
194
|
+
packetsReceivedPerSec = if ($stat) { $stat.PacketsReceivedPerSec } else { $null }
|
|
195
|
+
packetsSentPerSec = if ($stat) { $stat.PacketsSentPerSec } else { $null }
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
$active = $adapterList | Where-Object { $_.status -eq 2 -and $_.ipAddresses.Count -gt 0 }
|
|
199
|
+
[PSCustomObject]@{
|
|
200
|
+
adapters = $adapterList; activeAdapters = $active; adapterCount = $adapterList.Count; activeAdapterCount = @($active).Count
|
|
201
|
+
wifi = $wifi
|
|
202
|
+
totalDownloadBytesPerSec = ($active | Measure-Object -Property bytesReceivedPerSec -Sum).Sum
|
|
203
|
+
totalUploadBytesPerSec = ($active | Measure-Object -Property bytesSentPerSec -Sum).Sum
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function Get-BatteryInfo {
|
|
208
|
+
$batteries = Get-CimInstance Win32_Battery
|
|
209
|
+
$portable = Get-CimInstance Win32_PortableBattery
|
|
210
|
+
if (-not $batteries -or $batteries.Count -eq 0) {
|
|
211
|
+
return [PSCustomObject]@{ hasBattery = $false; batteries = @() }
|
|
212
|
+
}
|
|
213
|
+
$batteryList = @()
|
|
214
|
+
foreach ($b in $batteries) {
|
|
215
|
+
$p = $portable | Where-Object { $_.DeviceID -eq $b.DeviceID } | Select-Object -First 1
|
|
216
|
+
$designCap = if ($p -and $p.DesignCapacity) { $p.DesignCapacity } else { $null }
|
|
217
|
+
$fullCap = if ($p -and $p.FullChargeCapacity) { $p.FullChargeCapacity } else { $null }
|
|
218
|
+
$batteryList += [PSCustomObject]@{
|
|
219
|
+
name = $b.Name; deviceId = $b.DeviceID; status = $b.Status
|
|
220
|
+
batteryStatusValue = $b.BatteryStatus
|
|
221
|
+
batteryStatus = switch ($b.BatteryStatus) {
|
|
222
|
+
1 {'Discharging'} 2 {'AC Power'} 3 {'Fully Charged'} 6 {'Charging'} 11 {'Partially Charged'} default {'Unknown'}
|
|
223
|
+
}
|
|
224
|
+
chargePercent = $b.EstimatedChargeRemaining
|
|
225
|
+
estimatedRunTimeMinutes = if ($b.EstimatedRunTime -lt 10000) { $b.EstimatedRunTime } else { $null }
|
|
226
|
+
voltagemV = $b.DesignVoltage
|
|
227
|
+
designCapacitymWh = $designCap; fullChargeCapacitymWh = $fullCap
|
|
228
|
+
healthPercent = if ($designCap -and $fullCap -and $designCap -gt 0) { [math]::Round(($fullCap / $designCap) * 100, 2) } else { $null }
|
|
229
|
+
cycleCount = if ($p) { $p.CycleCount } else { $null }
|
|
230
|
+
manufacturer = if ($p) { $p.Manufacturer } else { $null }
|
|
231
|
+
chemistry = if ($p) { $p.Chemistry } else { $null }
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
[PSCustomObject]@{
|
|
235
|
+
hasBattery = $true; batteries = $batteryList
|
|
236
|
+
isCharging = @($batteryList | Where-Object { $_.batteryStatusValue -in 6,7,8,9 }).Count -gt 0
|
|
237
|
+
overallChargePercent = ($batteryList | Measure-Object -Property chargePercent -Average).Average
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function Get-GpuInfo {
|
|
242
|
+
$gpus = Get-CimInstance Win32_VideoController
|
|
243
|
+
$monitors = Get-CimInstance Win32_DesktopMonitor
|
|
244
|
+
$gpuList = @()
|
|
245
|
+
foreach ($g in $gpus) {
|
|
246
|
+
$hasResolution = $g.CurrentHorizontalResolution -ne $null -and $g.CurrentHorizontalResolution -gt 0
|
|
247
|
+
$nameLower = $g.Name.ToLower()
|
|
248
|
+
$isVirtual = (-not $hasResolution) -or $nameLower -match 'idd|virtual|mirror|dummy|wddm|remote'
|
|
249
|
+
if ($hasResolution) {
|
|
250
|
+
$displayMode = "$($g.CurrentHorizontalResolution)x$($g.CurrentVerticalResolution)@$($g.CurrentRefreshRate)Hz"
|
|
251
|
+
} elseif ($isVirtual) {
|
|
252
|
+
$displayMode = 'Virtual Display'
|
|
253
|
+
} else {
|
|
254
|
+
$displayMode = 'No Active Output'
|
|
255
|
+
}
|
|
256
|
+
$gpuList += [PSCustomObject]@{
|
|
257
|
+
name = $g.Name; isVirtual = $isVirtual; displayMode = $displayMode
|
|
258
|
+
adapterRAMMB = if ($g.AdapterRAM) { [math]::Round($g.AdapterRAM / 1MB, 2) } else { $null }
|
|
259
|
+
driverVersion = $g.DriverVersion; videoProcessor = $g.VideoProcessor
|
|
260
|
+
currentHorizontalResolution = $g.CurrentHorizontalResolution; currentVerticalResolution = $g.CurrentVerticalResolution
|
|
261
|
+
currentRefreshRate = $g.CurrentRefreshRate; currentBitsPerPixel = $g.CurrentBitsPerPixel
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
$monitorList = @()
|
|
265
|
+
foreach ($m in $monitors) {
|
|
266
|
+
$monitorList += [PSCustomObject]@{ name = $m.Name; monitorManufacturer = $m.MonitorManufacturer; screenHeight = $m.ScreenHeight; screenWidth = $m.ScreenWidth }
|
|
267
|
+
}
|
|
268
|
+
$primary = $gpuList | Where-Object { -not $_.isVirtual -and $_.currentHorizontalResolution } | Select-Object -First 1
|
|
269
|
+
[PSCustomObject]@{ gpus = $gpuList; monitors = $monitorList; gpuCount = $gpuList.Count; monitorCount = $monitorList.Count; primaryResolution = if ($primary) { $primary.displayMode } else { $null } }
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function Get-UsbInfo {
|
|
273
|
+
$controllers = Get-CimInstance Win32_USBController
|
|
274
|
+
$devices = Get-CimInstance Win32_PnPEntity | Where-Object { $_.PNPDeviceID -like 'USB\*' -and $_.Name -notlike 'USB Root Hub*' -and $_.Name -notlike 'USB Composite Device*' }
|
|
275
|
+
$controllerList = @()
|
|
276
|
+
foreach ($c in $controllers) { $controllerList += [PSCustomObject]@{ name = $c.Name; manufacturer = $c.Manufacturer; status = $c.Status } }
|
|
277
|
+
$deviceList = @()
|
|
278
|
+
foreach ($d in $devices) { $deviceList += [PSCustomObject]@{ name = $d.Name; description = $d.Description; manufacturer = $d.Manufacturer; status = $d.Status; service = $d.Service } }
|
|
279
|
+
[PSCustomObject]@{ controllers = $controllerList; controllerCount = $controllerList.Count; devices = $deviceList; deviceCount = $deviceList.Count }
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function Get-ProcessesInfo {
|
|
283
|
+
param($top = 0, $sortBy = 'memory', $includeCmdLine = $false)
|
|
284
|
+
$sortProp = switch ($sortBy) { 'cpu' { 'CPU' } 'name' { 'ProcessName' } default { 'WorkingSet64' } }
|
|
285
|
+
$procs = Get-Process | Sort-Object -Property $sortProp -Descending
|
|
286
|
+
if ($top -gt 0) { $procs = $procs | Select-Object -First $top }
|
|
287
|
+
$cmdLineMap = @{}
|
|
288
|
+
if ($includeCmdLine) {
|
|
289
|
+
foreach ($w in (Get-CimInstance Win32_Process | Select-Object ProcessId, CommandLine)) { $cmdLineMap[$w.ProcessId] = $w.CommandLine }
|
|
290
|
+
}
|
|
291
|
+
$list = @()
|
|
292
|
+
foreach ($p in $procs) {
|
|
293
|
+
$list += [PSCustomObject]@{
|
|
294
|
+
pid = $p.Id; name = $p.ProcessName
|
|
295
|
+
cpuSeconds = if ($p.CPU) { [math]::Round($p.CPU, 2) } else { 0 }
|
|
296
|
+
memoryMB = [math]::Round($p.WorkingSet64 / 1MB, 2); memoryPrivateMB = [math]::Round($p.PrivateMemorySize64 / 1MB, 2)
|
|
297
|
+
threads = $p.Threads.Count; handles = $p.HandleCount
|
|
298
|
+
startTime = if ($p.StartTime) { $p.StartTime.ToString('o') } else { $null }
|
|
299
|
+
path = $p.Path; company = $p.Company
|
|
300
|
+
commandLine = if ($includeCmdLine -and $cmdLineMap.ContainsKey($p.Id)) { $cmdLineMap[$p.Id] } else { $null }
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
[PSCustomObject]@{ total = (Get-Process).Count; returned = $list.Count; processes = $list }
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function Get-ServicesInfo {
|
|
307
|
+
param($runningOnly = $false)
|
|
308
|
+
$filter = if ($runningOnly) { '-Filter "State=''Running''"' } else { '' }
|
|
309
|
+
$services = Invoke-Expression "Get-CimInstance Win32_Service $filter" | Sort-Object Name
|
|
310
|
+
$list = @()
|
|
311
|
+
foreach ($s in $services) {
|
|
312
|
+
$list += [PSCustomObject]@{
|
|
313
|
+
name = $s.Name; displayName = $s.DisplayName; state = $s.State; status = $s.Status
|
|
314
|
+
startMode = $s.StartMode; startName = $s.StartName; pathName = $s.PathName
|
|
315
|
+
description = $s.Description; processId = $s.ProcessId; acceptStop = $s.AcceptStop; serviceType = $s.ServiceType
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
[PSCustomObject]@{ total = $list.Count; running = @($list | Where-Object { $_.state -eq 'Running' }).Count; stopped = @($list | Where-Object { $_.state -eq 'Stopped' }).Count; services = $list }
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function Get-AllInfo {
|
|
322
|
+
param($includeProcesses = $false, $includeServices = $false)
|
|
323
|
+
$result = [PSCustomObject]@{
|
|
324
|
+
timestamp = (Get-Date).ToString('o')
|
|
325
|
+
system = Get-SystemInfo
|
|
326
|
+
memory = Get-MemoryInfo
|
|
327
|
+
disk = Get-DiskInfo
|
|
328
|
+
network = Get-NetworkInfo
|
|
329
|
+
battery = Get-BatteryInfo
|
|
330
|
+
gpu = Get-GpuInfo
|
|
331
|
+
usb = Get-UsbInfo
|
|
332
|
+
}
|
|
333
|
+
if ($includeProcesses) { $result | Add-Member -NotePropertyName processes -NotePropertyValue (Get-ProcessesInfo -top 20) }
|
|
334
|
+
if ($includeServices) { $result | Add-Member -NotePropertyName services -NotePropertyValue (Get-ServicesInfo) }
|
|
335
|
+
return $result
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
# ========== 主循环 ==========
|
|
339
|
+
|
|
340
|
+
Write-Output "READY"
|
|
341
|
+
|
|
342
|
+
while ($true) {
|
|
343
|
+
$line = [Console]::In.ReadLine()
|
|
344
|
+
if ($null -eq $line) { break }
|
|
345
|
+
$line = $line.Trim()
|
|
346
|
+
if ($line -eq "exit" -or $line -eq "quit") { break }
|
|
347
|
+
if (-not $line) { continue }
|
|
348
|
+
|
|
349
|
+
$parts = $line -split ':'
|
|
350
|
+
$cmd = $parts[0]
|
|
351
|
+
|
|
352
|
+
try {
|
|
353
|
+
$data = switch ($cmd) {
|
|
354
|
+
"system" { Get-SystemInfo }
|
|
355
|
+
"memory" { Get-MemoryInfo }
|
|
356
|
+
"disk" { Get-DiskInfo }
|
|
357
|
+
"network" {
|
|
358
|
+
$incWifi = if ($parts[1]) { [bool]::Parse($parts[1]) } else { $false }
|
|
359
|
+
Get-NetworkInfo -includeWifi $incWifi
|
|
360
|
+
}
|
|
361
|
+
"battery" { Get-BatteryInfo }
|
|
362
|
+
"gpu" { Get-GpuInfo }
|
|
363
|
+
"usb" { Get-UsbInfo }
|
|
364
|
+
"processes" {
|
|
365
|
+
$top = if ($parts[1]) { [int]$parts[1] } else { 0 }
|
|
366
|
+
$sortBy = if ($parts[2]) { $parts[2] } else { "memory" }
|
|
367
|
+
$includeCmd = if ($parts[3]) { [bool]::Parse($parts[3]) } else { $false }
|
|
368
|
+
Get-ProcessesInfo -top $top -sortBy $sortBy -includeCmdLine $includeCmd
|
|
369
|
+
}
|
|
370
|
+
"services" {
|
|
371
|
+
$runningOnly = if ($parts[1]) { [bool]::Parse($parts[1]) } else { $false }
|
|
372
|
+
Get-ServicesInfo -runningOnly $runningOnly
|
|
373
|
+
}
|
|
374
|
+
"all" {
|
|
375
|
+
$incProc = if ($parts[1]) { [bool]::Parse($parts[1]) } else { $false }
|
|
376
|
+
$incSvc = if ($parts[2]) { [bool]::Parse($parts[2]) } else { $false }
|
|
377
|
+
Get-AllInfo -includeProcesses $incProc -includeServices $incSvc
|
|
378
|
+
}
|
|
379
|
+
default { [PSCustomObject]@{ error = "Unknown command: $cmd" } }
|
|
380
|
+
}
|
|
381
|
+
Write-Output ($data | ConvertTo-Json -Depth 8 -Compress)
|
|
382
|
+
} catch {
|
|
383
|
+
$errMsg = $_.Exception.Message -replace '"', '\"'
|
|
384
|
+
Write-Output "{`"error`":`"$errMsg`"}"
|
|
385
|
+
}
|
|
386
|
+
}
|