@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.
Files changed (47) hide show
  1. package/README.md +907 -0
  2. package/bin/OpenHardwareMonitorLib.dll +0 -0
  3. package/index.js +192 -0
  4. package/lib/allhardware.js +35 -0
  5. package/lib/backend-usage.js +103 -0
  6. package/lib/battery.js +76 -0
  7. package/lib/disk-watcher.js +156 -0
  8. package/lib/disk.js +77 -0
  9. package/lib/gpu.js +70 -0
  10. package/lib/hardware-service.js +284 -0
  11. package/lib/memory.js +59 -0
  12. package/lib/monitor.js +176 -0
  13. package/lib/network.js +75 -0
  14. package/lib/ohm-daemon.js +208 -0
  15. package/lib/ohm.js +41 -0
  16. package/lib/perfctr.js +43 -0
  17. package/lib/process-icon.js +76 -0
  18. package/lib/process-ops.js +218 -0
  19. package/lib/processes.js +232 -0
  20. package/lib/ps.js +93 -0
  21. package/lib/public-ip.js +124 -0
  22. package/lib/services.js +43 -0
  23. package/lib/sysinfo-daemon.js +188 -0
  24. package/lib/system.js +77 -0
  25. package/lib/usb.js +49 -0
  26. package/lib/win32-procs.js +241 -0
  27. package/package.json +50 -0
  28. package/scripts/get-all-hardware.ps1 +98 -0
  29. package/scripts/get-lhm-temp.ps1 +119 -0
  30. package/scripts/ohm-daemon.ps1 +141 -0
  31. package/scripts/sysinfo-daemon.ps1 +386 -0
  32. package/test/check-bugs.js +182 -0
  33. package/test/public/gj.pay.ali.jpg +0 -0
  34. package/test/public/gj.pay.wx.jpg +0 -0
  35. package/test/public/index.html +996 -0
  36. package/test/public/zdl.pay.ali.jpg +0 -0
  37. package/test/public/zdl.pay.wx.jpg +0 -0
  38. package/test/scan-encoding.js +77 -0
  39. package/test/server.js +255 -0
  40. package/test/test-all.js +180 -0
  41. package/test/test-daemon.js +51 -0
  42. package/test/test-kill-name.js +30 -0
  43. package/test/test-monitor.js +78 -0
  44. package/test/test-new-features.js +93 -0
  45. package/test/test-service.js +91 -0
  46. package/test/test-sysinfo-daemon.js +95 -0
  47. package/test/test.js +63 -0
package/lib/system.js ADDED
@@ -0,0 +1,77 @@
1
+ const { runPsJson } = require('./ps');
2
+
3
+ /**
4
+ * 获取系统信息:OS、计算机、BIOS、主板、CPU、开机时间、电源计划
5
+ */
6
+ async function getSystemInfo() {
7
+ const cmd = `
8
+ $os = Get-CimInstance Win32_OperatingSystem
9
+ $cs = Get-CimInstance Win32_ComputerSystem
10
+ $bios = Get-CimInstance Win32_BIOS
11
+ $board = Get-CimInstance Win32_BaseBoard
12
+ $cpu = Get-CimInstance Win32_Processor | Select-Object -First 1
13
+ $powerPlan = powercfg /getactivescheme | ForEach-Object { ($_ -replace '.*\\(|\\).*', '') }
14
+ $bootTime = $os.LastBootUpTime
15
+ $uptime = (Get-Date) - $bootTime
16
+
17
+ [PSCustomObject]@{
18
+ os = [PSCustomObject]@{
19
+ caption = $os.Caption
20
+ version = $os.Version
21
+ buildNumber = $os.BuildNumber
22
+ architecture = $os.OSArchitecture
23
+ installDate = $os.InstallDate.ToString('o')
24
+ lastBootUpTime = $bootTime.ToString('o')
25
+ uptimeSeconds = [math]::Round($uptime.TotalSeconds)
26
+ uptimeDays = [math]::Round($uptime.TotalDays, 2)
27
+ systemDrive = $os.SystemDrive
28
+ windowsDirectory = $os.WindowsDirectory
29
+ }
30
+ computer = [PSCustomObject]@{
31
+ manufacturer = $cs.Manufacturer
32
+ model = $cs.Model
33
+ name = $cs.Name
34
+ domain = $cs.Domain
35
+ workgroup = $cs.Workgroup
36
+ totalPhysicalMemoryGB = [math]::Round($cs.TotalPhysicalMemory / 1GB, 2)
37
+ numberOfProcessors = $cs.NumberOfProcessors
38
+ numberOfLogicalProcessors = $cs.NumberOfLogicalProcessors
39
+ systemType = $cs.SystemType
40
+ pcSystemType = $cs.PCSystemType
41
+ }
42
+ bios = [PSCustomObject]@{
43
+ manufacturer = $bios.Manufacturer
44
+ version = $bios.SMBIOSBIOSVersion
45
+ releaseDate = if ($bios.ReleaseDate) { $bios.ReleaseDate.ToString('o') } else { $null }
46
+ serialNumber = $bios.SerialNumber
47
+ }
48
+ motherboard = [PSCustomObject]@{
49
+ manufacturer = $board.Manufacturer
50
+ product = $board.Product
51
+ version = $board.Version
52
+ serialNumber = $board.SerialNumber
53
+ }
54
+ cpu = [PSCustomObject]@{
55
+ name = $cpu.Name
56
+ manufacturer = $cpu.Manufacturer
57
+ description = $cpu.Description
58
+ architecture = $cpu.Architecture
59
+ numberOfCores = $cpu.NumberOfCores
60
+ numberOfLogicalProcessors = $cpu.NumberOfLogicalProcessors
61
+ maxClockSpeedMHz = $cpu.MaxClockSpeed
62
+ currentClockSpeedMHz = $cpu.CurrentClockSpeed
63
+ l2CacheKB = $cpu.L2CacheSize
64
+ l3CacheKB = $cpu.L3CacheSize
65
+ socketDesignation = $cpu.SocketDesignation
66
+ processorId = $cpu.ProcessorId
67
+ virtualizationEnabled = $cpu.VirtualizationFirmwareEnabled
68
+ }
69
+ powerPlan = $powerPlan
70
+ timezone = (Get-TimeZone).Id
71
+ currentTime = (Get-Date).ToString('o')
72
+ } | ConvertTo-Json -Depth 6 -Compress
73
+ `;
74
+ return runPsJson(cmd);
75
+ }
76
+
77
+ module.exports = { getSystemInfo };
package/lib/usb.js ADDED
@@ -0,0 +1,49 @@
1
+ const { runPsJson } = require('./ps');
2
+
3
+ /**
4
+ * 获取 USB 设备列表
5
+ */
6
+ async function getUsbInfo() {
7
+ const cmd = `
8
+ $controllers = Get-CimInstance Win32_USBController
9
+ $devices = Get-CimInstance Win32_PnPEntity | Where-Object {
10
+ $_.PNPDeviceID -like 'USB\\*' -and $_.Name -notlike 'USB Root Hub*' -and $_.Name -notlike 'USB Composite Device*'
11
+ }
12
+
13
+ $controllerList = @()
14
+ foreach ($c in $controllers) {
15
+ $controllerList += [PSCustomObject]@{
16
+ name = $c.Name
17
+ manufacturer = $c.Manufacturer
18
+ status = $c.Status
19
+ deviceId = $c.DeviceID
20
+ pnpDeviceId = $c.PNPDeviceID
21
+ }
22
+ }
23
+
24
+ $deviceList = @()
25
+ foreach ($d in $devices) {
26
+ $deviceList += [PSCustomObject]@{
27
+ name = $d.Name
28
+ description = $d.Description
29
+ manufacturer = $d.Manufacturer
30
+ status = $d.Status
31
+ service = $d.Service
32
+ deviceId = $d.DeviceID
33
+ pnpDeviceId = $d.PNPDeviceID
34
+ classGuid = $d.ClassGuid
35
+ hardwareId = if ($d.HardwareID) { $d.HardwareID[0] } else { $null }
36
+ }
37
+ }
38
+
39
+ [PSCustomObject]@{
40
+ controllers = $controllerList
41
+ controllerCount = $controllerList.Count
42
+ devices = $deviceList
43
+ deviceCount = $deviceList.Count
44
+ } | ConvertTo-Json -Depth 4 -Compress
45
+ `;
46
+ return runPsJson(cmd, { timeout: 15000 });
47
+ }
48
+
49
+ module.exports = { getUsbInfo };
@@ -0,0 +1,241 @@
1
+ /**
2
+ * win32-procs.js — koffi 直调 Win32 API 枚举进程
3
+ *
4
+ * 性能:264 进程 ~16ms(vs PowerShell WMI ~590ms / 常驻 WMI ~115ms)
5
+ * 高频调用友好:进程内调用,无子进程开销
6
+ *
7
+ * 涉及 API:
8
+ * kernel32: CreateToolhelp32Snapshot / Process32FirstW / Process32NextW
9
+ * OpenProcess / CloseHandle
10
+ * QueryFullProcessImageNameW
11
+ * GetProcessTimes
12
+ * psapi: GetProcessMemoryInfo
13
+ *
14
+ * CPU% 口径:与任务管理器一致
15
+ * (Δkernel+Δuser 100ns ticks) / (Δwall 100ns ticks) × 100
16
+ * 单进程 CPU% 上限 = 核心数 × 100%
17
+ */
18
+
19
+ const koffi = require('koffi');
20
+ const { existsSync, statSync } = require('fs');
21
+
22
+ const TH32CS_SNAPPROCESS = 0x00000002;
23
+ const PROCESS_QUERY_LIMITED_INFORMATION = 0x1000;
24
+ const PROCESS_VM_READ = 0x0010;
25
+ const MAX_PATH = 260;
26
+
27
+ // ---- Win32 结构体 ----
28
+ const PROCESSENTRY32W = koffi.struct('PROCESSENTRY32W', {
29
+ dwSize: 'uint32',
30
+ cntUsage: 'uint32',
31
+ th32ProcessID: 'uint32',
32
+ th32DefaultHeapID: 'uint64',
33
+ th32ModuleID: 'uint32',
34
+ cntThreads: 'uint32',
35
+ th32ParentProcessID: 'uint32',
36
+ pcPriClassBase: 'int32',
37
+ dwFlags: 'uint32',
38
+ szExeFile: koffi.array('uint16', MAX_PATH)
39
+ });
40
+
41
+ const FILETIME = koffi.struct('FILETIME', {
42
+ dwLowDateTime: 'uint32',
43
+ dwHighDateTime: 'uint32'
44
+ });
45
+
46
+ const PROCESS_MEMORY_COUNTERS = koffi.struct('PROCESS_MEMORY_COUNTERS', {
47
+ cb: 'uint32',
48
+ PageFaultCount: 'uint32',
49
+ PeakWorkingSetSize: 'uint64',
50
+ WorkingSetSize: 'uint64',
51
+ QuotaPeakPagedPoolUsage: 'uint64',
52
+ QuotaPagedPoolUsage: 'uint64',
53
+ QuotaPeakNonPagedPoolUsage: 'uint64',
54
+ QuotaNonPagedPoolUsage: 'uint64',
55
+ PagefileUsage: 'uint64',
56
+ PeakPagefileUsage: 'uint64'
57
+ });
58
+
59
+ // ---- 加载 DLL 并声明函数 ----
60
+ const kernel32 = koffi.load('kernel32.dll');
61
+ const psapi = koffi.load('psapi.dll');
62
+
63
+ const PE32_PTR = koffi.inout(koffi.pointer(PROCESSENTRY32W));
64
+ const CreateToolhelp32Snapshot = kernel32.func('CreateToolhelp32Snapshot', 'void *', ['uint32', 'uint32']);
65
+ const Process32FirstW = kernel32.func('Process32FirstW', 'int', ['void *', PE32_PTR]);
66
+ const Process32NextW = kernel32.func('Process32NextW', 'int', ['void *', PE32_PTR]);
67
+ const CloseHandle = kernel32.func('CloseHandle', 'int', ['void *']);
68
+ const OpenProcess = kernel32.func('OpenProcess', 'void *', ['uint32', 'int', 'uint32']);
69
+ const QueryFullProcessImageNameW = kernel32.func(
70
+ 'QueryFullProcessImageNameW',
71
+ 'int',
72
+ ['void *', 'uint32', koffi.out(koffi.pointer('uint16')), koffi.inout(koffi.pointer('uint32'))]
73
+ );
74
+ const FT_PTR = koffi.out(koffi.pointer(FILETIME));
75
+ const GetProcessTimes = kernel32.func('GetProcessTimes', 'int', ['void *', FT_PTR, FT_PTR, FT_PTR, FT_PTR]);
76
+ const PMC_PTR = koffi.inout(koffi.pointer(PROCESS_MEMORY_COUNTERS));
77
+ const GetProcessMemoryInfo = psapi.func('GetProcessMemoryInfo', 'int', ['void *', PMC_PTR, 'uint32']);
78
+
79
+ // ---- 辅助函数 ----
80
+ function wcharToStr(arr) {
81
+ let end = 0;
82
+ while (end < arr.length && arr[end] !== 0) end++;
83
+ if (end === 0) return '';
84
+ return Buffer.from(arr.slice(0, end).buffer).toString('utf16le');
85
+ }
86
+
87
+ function filetimeToNumber(ft) {
88
+ return (ft.dwHighDateTime * 0x100000000) + ft.dwLowDateTime;
89
+ }
90
+
91
+ // ---- CPU% 基线缓存 ----
92
+ let cpuBaseline = null; // Map<pid, { kernel, user, wallTime }>
93
+
94
+ /**
95
+ * 枚举所有进程(原始数据)
96
+ * @returns {Array<{pid, name, threads, parentPid, workingSet, privateUsage, exePath, kernelTime, userTime}>}
97
+ */
98
+ function sampleProcesses() {
99
+ const snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
100
+ if (!snapshot || snapshot === 0) {
101
+ throw new Error('CreateToolhelp32Snapshot 失败');
102
+ }
103
+
104
+ const entry = { dwSize: PROCESSENTRY32W.size };
105
+ const processes = [];
106
+
107
+ if (Process32FirstW(snapshot, entry)) {
108
+ do {
109
+ const pid = entry.th32ProcessID;
110
+ const name = wcharToStr(entry.szExeFile);
111
+ const threads = entry.cntThreads;
112
+ const parentPid = entry.th32ParentProcessID;
113
+
114
+ let workingSet = 0;
115
+ let privateUsage = 0;
116
+ let exePath = '';
117
+ let kernelTime = 0;
118
+ let userTime = 0;
119
+
120
+ // 打开进程获取详细信息(系统进程可能失败,跳过即可)
121
+ const handle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ, 0, pid);
122
+ if (handle && handle !== 0) {
123
+ try {
124
+ // exe 路径
125
+ const buf = Buffer.alloc(MAX_PATH * 2);
126
+ const sizePtr = [MAX_PATH];
127
+ if (QueryFullProcessImageNameW(handle, 0, buf, sizePtr)) {
128
+ exePath = buf.toString('utf16le', 0, sizePtr[0] * 2).replace(/\0+$/, '');
129
+ }
130
+ } catch (e) { /* 忽略 */ }
131
+
132
+ try {
133
+ // 进程时间(用于 CPU%)
134
+ const ct = {};
135
+ const et = {};
136
+ const kt = {};
137
+ const ut = {};
138
+ if (GetProcessTimes(handle, ct, et, kt, ut)) {
139
+ kernelTime = filetimeToNumber(kt);
140
+ userTime = filetimeToNumber(ut);
141
+ }
142
+ } catch (e) { /* 忽略 */ }
143
+
144
+ try {
145
+ // 内存信息
146
+ const pmc = { cb: PROCESS_MEMORY_COUNTERS.size };
147
+ if (GetProcessMemoryInfo(handle, pmc, PROCESS_MEMORY_COUNTERS.size)) {
148
+ workingSet = pmc.WorkingSetSize;
149
+ privateUsage = pmc.PagefileUsage;
150
+ }
151
+ } catch (e) { /* 忽略 */ }
152
+
153
+ CloseHandle(handle);
154
+ }
155
+
156
+ processes.push({
157
+ pid,
158
+ name,
159
+ threads,
160
+ parentPid,
161
+ workingSet,
162
+ privateUsage,
163
+ exePath,
164
+ kernelTime,
165
+ userTime
166
+ });
167
+ } while (Process32NextW(snapshot, entry));
168
+ }
169
+
170
+ CloseHandle(snapshot);
171
+ return processes;
172
+ }
173
+
174
+ /**
175
+ * 计算每个进程的 CPU%(基于两次采样差值)
176
+ * 第一次调用时基线为空,返回全 0;第二次起正常
177
+ * @param {Array} raw - sampleProcesses() 的返回值
178
+ * @returns {Map<pid, cpuPercent>}
179
+ */
180
+ function computeCpuPercent(raw) {
181
+ const now = Date.now();
182
+ const cpuMap = new Map();
183
+
184
+ if (!cpuBaseline) {
185
+ // 第一次采样,建立基线
186
+ cpuBaseline = new Map();
187
+ for (const p of raw) {
188
+ cpuBaseline.set(p.pid, { kernel: p.kernelTime, user: p.userTime, wallTime: now });
189
+ cpuMap.set(p.pid, 0);
190
+ }
191
+ return cpuMap;
192
+ }
193
+
194
+ const newBaseline = new Map();
195
+ for (const p of raw) {
196
+ const prev = cpuBaseline.get(p.pid);
197
+ let cpuPercent = 0;
198
+ if (prev) {
199
+ const dKernel = p.kernelTime - prev.kernel;
200
+ const dUser = p.userTime - prev.user;
201
+ const dWall = (now - prev.wallTime) * 10000; // ms → 100ns ticks
202
+ if (dWall > 0) {
203
+ cpuPercent = ((dKernel + dUser) / dWall) * 100;
204
+ }
205
+ }
206
+ cpuMap.set(p.pid, Math.max(0, cpuPercent));
207
+ newBaseline.set(p.pid, { kernel: p.kernelTime, user: p.userTime, wallTime: now });
208
+ }
209
+ cpuBaseline = newBaseline;
210
+ return cpuMap;
211
+ }
212
+
213
+ /**
214
+ * 重置 CPU% 基线(下次采样重新建立)
215
+ */
216
+ function resetCpuBaseline() {
217
+ cpuBaseline = null;
218
+ }
219
+
220
+ /**
221
+ * 获取 exe 文件的安装时间(创建时间)
222
+ * @param {string} exePath
223
+ * @returns {string} 'YYYY-MM-DD' 或空串
224
+ */
225
+ function getInstallTime(exePath) {
226
+ if (!exePath || !existsSync(exePath)) return '';
227
+ try {
228
+ const stat = statSync(exePath);
229
+ const d = stat.birthtime;
230
+ return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
231
+ } catch {
232
+ return '';
233
+ }
234
+ }
235
+
236
+ module.exports = {
237
+ sampleProcesses,
238
+ computeCpuPercent,
239
+ resetCpuBaseline,
240
+ getInstallTime
241
+ };
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@daimazun/hardware-info",
3
+ "version": "1.0.0",
4
+ "description": "Cross-platform hardware information & monitor library — CPU temperature/load/clock/power, memory, disk, motherboard, GPU via OpenHardwareMonitor (Windows) with Linux/macOS support planned",
5
+ "type": "commonjs",
6
+ "main": "index.js",
7
+ "os": [
8
+ "win32"
9
+ ],
10
+ "engines": {
11
+ "node": ">=14.0.0"
12
+ },
13
+ "keywords": [
14
+ "cpu",
15
+ "temperature",
16
+ "windows",
17
+ "openhardwaremonitor",
18
+ "hardware",
19
+ "monitor",
20
+ "sensor",
21
+ "daemon",
22
+ "msr",
23
+ "dts"
24
+ ],
25
+ "license": "MIT",
26
+ "author": "高健 <gaojianstyle@163.com>",
27
+ "scripts": {
28
+ "test": "node test/test.js",
29
+ "test:daemon": "node test/test-daemon.js",
30
+ "test:cpu-monitor": "node test/test-monitor.js",
31
+ "test:all": "node test/test-all.js",
32
+ "test:new": "node test/test-new-features.js",
33
+ "test:monitor": "node test/test-service.js",
34
+ "server": "node test/server.js"
35
+ },
36
+ "files": [
37
+ "index.js",
38
+ "lib/",
39
+ "scripts/",
40
+ "bin/",
41
+ "README.md",
42
+ "!**/*.png",
43
+ "test/"
44
+ ],
45
+ "dependencies": {
46
+ "express": "^5.2.1",
47
+ "fkill": "^10.0.3",
48
+ "koffi": "^3.2.1"
49
+ }
50
+ }
@@ -0,0 +1,98 @@
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 = $true
33
+ $monitor.GPUEnabled = $true
34
+ $monitor.HDDEnabled = $true
35
+ $monitor.RAMEnabled = $true
36
+ $monitor.FanControllerEnabled = $true
37
+
38
+ try {
39
+ $monitor.Open()
40
+ } catch {
41
+ Write-Error "Failed to open monitor (needs admin?): $_"
42
+ exit 1
43
+ }
44
+
45
+ $hardwareList = @()
46
+
47
+ function Get-SensorValue {
48
+ param($sensor)
49
+ $val = $sensor.Value
50
+ if ($null -eq $val) { return $null }
51
+ return [math]::Round([double]$val, 2)
52
+ }
53
+
54
+ function Walk-Hardware {
55
+ param($hw, $parentPath = "")
56
+ $hw.Update()
57
+
58
+ $sensors = @()
59
+ foreach ($sensor in $hw.Sensors) {
60
+ $val = Get-SensorValue $sensor
61
+ if ($null -ne $val) {
62
+ $sensors += [PSCustomObject]@{
63
+ name = $sensor.Name
64
+ type = $sensor.SensorType.ToString()
65
+ value = $val
66
+ }
67
+ }
68
+ }
69
+
70
+ $hwInfo = [PSCustomObject]@{
71
+ hardwareType = $hw.HardwareType.ToString()
72
+ name = $hw.Name
73
+ identifier = $hw.Identifier.ToString()
74
+ sensors = $sensors
75
+ subHardware = @()
76
+ }
77
+
78
+ foreach ($sub in $hw.SubHardware) {
79
+ $subInfo = Walk-Hardware $sub
80
+ $hwInfo.subHardware += $subInfo
81
+ }
82
+
83
+ return $hwInfo
84
+ }
85
+
86
+ foreach ($hw in $monitor.Hardware) {
87
+ $hardwareList += Walk-Hardware $hw
88
+ }
89
+
90
+ $monitor.Close()
91
+
92
+ $result = [PSCustomObject]@{
93
+ source = "openhardwaremonitor"
94
+ timestamp = (Get-Date).ToString("o")
95
+ hardware = $hardwareList
96
+ }
97
+
98
+ $result | ConvertTo-Json -Depth 10 -Compress
@@ -0,0 +1,119 @@
1
+ param(
2
+ [string]$DllPath = ""
3
+ )
4
+
5
+
6
+ # UTF-8 输出,解决中文乱码
7
+ [Console]::OutputEncoding = [System.Text.Encoding]::UTF8
8
+ $OutputEncoding = [System.Text.Encoding]::UTF8
9
+ chcp 65001 > $null
10
+
11
+ $ErrorActionPreference = "Stop"
12
+
13
+ if (-not $DllPath) {
14
+ $DllPath = Join-Path $PSScriptRoot "..\bin\OpenHardwareMonitorLib.dll"
15
+ }
16
+ $DllPath = (Resolve-Path $DllPath).Path
17
+
18
+ if (-not (Test-Path $DllPath)) {
19
+ Write-Error "DLL not found: $DllPath"
20
+ exit 1
21
+ }
22
+
23
+ try {
24
+ Unblock-File -LiteralPath $DllPath -ErrorAction SilentlyContinue
25
+ Add-Type -LiteralPath $DllPath
26
+ } catch {
27
+ Write-Error "Failed to load DLL: $_"
28
+ exit 1
29
+ }
30
+
31
+ $monitor = New-Object OpenHardwareMonitor.Hardware.Computer
32
+ $monitor.CPUEnabled = $true
33
+ $monitor.MainboardEnabled = $false
34
+ $monitor.GPUEnabled = $false
35
+ $monitor.HDDEnabled = $false
36
+ $monitor.RAMEnabled = $false
37
+ $monitor.FanControllerEnabled = $false
38
+
39
+ try {
40
+ $monitor.Open()
41
+ } catch {
42
+ Write-Error "Failed to open monitor (needs admin?): $_"
43
+ exit 1
44
+ }
45
+
46
+ $cpuName = ""
47
+ $allSensors = @()
48
+ $subSensors = @()
49
+
50
+ function Walk-Hardware {
51
+ param($hw)
52
+ $hw.Update()
53
+ foreach ($sensor in $hw.Sensors) {
54
+ $val = $sensor.Value
55
+ if ($null -ne $val) {
56
+ $script:allSensors += [PSCustomObject]@{
57
+ name = $sensor.Name
58
+ type = $sensor.SensorType.ToString()
59
+ value = [math]::Round([double]$val, 2)
60
+ }
61
+ }
62
+ }
63
+ foreach ($sub in $hw.SubHardware) {
64
+ Walk-Hardware $sub
65
+ }
66
+ }
67
+
68
+ foreach ($hw in $monitor.Hardware) {
69
+ if ($hw.HardwareType -eq [OpenHardwareMonitor.Hardware.HardwareType]::CPU) {
70
+ $cpuName = $hw.Name
71
+ Walk-Hardware $hw
72
+ }
73
+ }
74
+
75
+ $monitor.Close()
76
+
77
+ if ($allSensors.Count -eq 0) {
78
+ Write-Error "OHM: no sensors (likely no admin / driver failed)"
79
+ exit 1
80
+ }
81
+
82
+ function Get-SensorsByType {
83
+ param($type)
84
+ return @($allSensors | Where-Object { $_.type -eq $type } | ForEach-Object {
85
+ [PSCustomObject]@{ name = $_.name; value = $_.value }
86
+ })
87
+ }
88
+
89
+ $tempSensors = Get-SensorsByType "Temperature"
90
+ $loadSensors = Get-SensorsByType "Load"
91
+ $clockSensors = Get-SensorsByType "Clock"
92
+ $powerSensors = Get-SensorsByType "Power"
93
+
94
+ # 没有温度传感器说明驱动没加载成功(大概率非管理员),报错让上层降级
95
+ if ($tempSensors.Count -eq 0) {
96
+ Write-Error "OHM: no temperature sensors (likely no admin privileges, WinRing0 driver not loaded)"
97
+ exit 1
98
+ }
99
+ $coreTemps = @($tempSensors | Where-Object { $_.name -match "Core" })
100
+ $packageTemp = ($tempSensors | Where-Object { $_.name -match "Package" } | Select-Object -First 1).value
101
+ $maxCoreTemp = if ($coreTemps.Count -gt 0) { ($coreTemps | Measure-Object -Property value -Maximum).Maximum } else { $null }
102
+
103
+ $result = [PSCustomObject]@{
104
+ source = "openhardwaremonitor"
105
+ isCoreTemp = $true
106
+ cpuName = $cpuName
107
+ temperature = [PSCustomObject]@{
108
+ cores = $coreTemps
109
+ package = $packageTemp
110
+ maxCore = $maxCoreTemp
111
+ }
112
+ load = $loadSensors
113
+ clock = $clockSensors
114
+ power = $powerSensors
115
+ allSensors = $allSensors
116
+ timestamp = (Get-Date).ToString("o")
117
+ }
118
+
119
+ $result | ConvertTo-Json -Depth 6 -Compress