在屏幕顶部正中用 1~2 行显示实时硬件状态,数据全部自选,鼠标穿透、不挡操作。
Windows 10/11 · .NET 8 · WinForms · MIT 协议
CPU温度 48°C CPU占用 14% GPU温度 45°C GPU占用 2%
内存 12.3/31.8GB 显存 0.9/6.0GB 帧率 41FPS ↓ 14.4 KB/s ↑ 1.60 MB/s
它能做什么
- CPU:温度、占用率、频率、功耗、风扇转速
- GPU:温度、热点温度、占用率、功耗、风扇转速、显存占用 —— 走 NVAPI,不需要管理员权限
- 内存 / 显存:已用 / 总量、占用率
- 网速:实时上下行,自动挑流量最大的网卡(也可指定或全部合计)
- 显示:显示器刷新率,以及用 DXGI 桌面复制实测的实时帧率
- 任意传感器:LibreHardwareMonitor 枚举到的每个传感器(每核心频率、各路电压、主板温度、硬盘温度…)都能单独加到条上
- 1~2 行随便排:显示哪些数据、放第几行、什么顺序、叫什么名字、用什么模板格式,全部在设置界面里点几下搞定
- 不挡操作:默认鼠标穿透,点击直接透到后面的窗口;鼠标移到它上面时强烈淡化到 15%
- 常驻托盘:拖动定位、字号快捷增减、开机自启(提权时创建计划任务,开机不再弹 UAC)
下载即用
下载后可用 SHA-256 对一下,确认拿到的是原封不动的那一份:校验和清单。
⚠️ 先装 .NET 8 桌面运行时,否则打不开
这个包是框架依赖版本,体积只有 2.9 MB,因为它复用系统的 .NET 运行时。 没装的话双击会没有任何反应或提示缺少运行时。
请先安装一次(只需装 Runtime,不用装 SDK,装完永久有效):
下载 .NET 8 桌面运行时 → 选 Desktop Runtime 的 x64 版本
装好后回来解压上面的 ZIP 即可。
判断依据:程序用的是
net8.0-windows配SelfContained=false, 所以必须有 Microsoft.WindowsDesktop.App 8.x。 如果你的电脑已经装过 .NET 8 桌面运行时,可以跳过这一步。
从源码编译(开发者)
git clone https://github.com/leerogerstheman/SysMonitorBar.git
cd SysMonitorBar
build.cmd :: 需要 .NET 8 SDK,脚本会自动找
启动-管理员.cmd :: 或 启动.cmd(不提权)
app\目录是编译产物,不在仓库里,所以从源码走必须先跑一次build.cmd。
想直接从 GitHub 拿文件、或看历史版本:Releases
使用方法
各个启动脚本
| 文件 | 作用 |
|---|---|
| 启动-管理员.cmd | 推荐。弹 UAC 提权后启动,能读到 CPU 风扇等需要内核驱动的数据 |
| 启动.cmd | 不提权启动。实测 CPU 温度 / GPU / 内存 / 显存 / 网速 / 刷新率 / 帧率 全部正常 |
| 设置.cmd | 打开设置界面 |
| 退出.cmd | 结束程序 |
| build.cmd | 改过源码后重新编译到 app\ |
启动后常驻系统托盘(任务栏右下角,可能收在 ^ 折叠区里):
- 左键双击 → 打开设置
- 右键菜单 → 显示/隐藏、设置、拖动调整位置、立即刷新、字号增减、打开配置文件夹、重新加载配置、退出
调整位置与淡化
悬浮条默认完全紧贴屏幕上边缘,水平居中。
默认鼠标穿透,所以直接拖是拖不动的。正确做法:托盘右键 → 「拖动调整位置」 → 用鼠标把条拖到想要的地方 → 再点一次该菜单项锁定。 也可以在设置 → 「位置与行为」里用水平/垂直偏移精确调。
鼠标移到悬浮条范围内时它会强烈淡化(默认淡到 15%,几乎看不见),移开立刻恢复,过渡约 150ms。 设置 →「外观」里可以关掉,或把「淡化后不透明度」改成别的值(填 0 就是完全隐形)。
鼠标穿透状态下窗口收不到
MouseEnter/Leave,所以程序是轮询鼠标屏幕坐标判断的,穿透与否都能生效。
调整字号
- 托盘右键 → 「字号 增大 / 减小 / 重置」,一键快速调
- 设置 →「外观」→ 字号,精确调(6~40,步进 0.5)
改完立刻生效并写入配置。
能显示哪些数据
设置 → 「显示内容」,两步:
① 加数据:在左边「可选数据」里选中一项,再点 「→ 加入第一行」 或 「→ 加入第二行」。 ② 调整:右边每行各有一列按钮 —— 上移 / 下移 / 移除 / 移到另一行。 在行列表上右键也有同样的菜单。
选中任意一行里的项目后,下面的「显示名」「模板」可以直接改它的名称和显示格式。
内置指标
| 分组 | 指标 |
|---|---|
| 处理器 | CPU 温度、占用率、风扇转速、频率、功耗 |
| 显卡 | GPU 温度、占用率、热点温度、风扇转速(%)、风扇(转/分)、功耗 |
| 内存/显存 | 内存占用、内存占用率、显存占用、显存占用率 |
| 网络 | 下载速度、上传速度、上下行合计 |
| 显示 | 实时帧率(实测)、显示器刷新率 |
| 其他 | 当前时间、当前日期、开机时长 |
硬件传感器
左边列表下半部分会列出 LibreHardwareMonitor 枚举到的每一个传感器 (电压、电流、每核心频率、主板温度、硬盘温度……按硬件分组),都能单独加进悬浮条。 列表里的「当前值」列实时显示读数,方便挑。
显示模板
每条数据都可以自定义模板:
| 占位符 | 含义 |
|---|---|
{label} | 名称(可在设置里改成任意文字,会用高亮色显示) |
{value} | 数值 |
{unit} | 单位 |
例:模板 CPU {value}{unit} + 显示名 CPU → CPU 62°C
本机的数据可用性(重要)
程序会自动把读不到的数据隐藏掉(设置里可关掉这个行为)。这是开发机实测结果:
| 数据 | 状态 | 说明 |
|---|---|---|
| CPU 温度 | ✅ | 双数据源,见下 |
| CPU 占用、内存、显存、网速 | ✅ | 无限制 |
| GPU 温度 / 占用 / 热点 / 功耗 | ✅ | 走 NVAPI,不需要管理员 |
| 显示器刷新率 | ✅ | 直接读当前显示模式 |
| 实时帧率 | ✅ | DXGI 桌面复制实测,按刷新率封顶 |
| CPU 风扇 / GPU 风扇 | ❌ 本机没有该传感器 | 见下 |
| CPU 频率 / CPU 功耗 | ⚠️ 本机读不到 | 依赖 LHM 内核驱动,本机驱动未加载 |
CPU 温度:双数据源
程序按顺序尝试两个来源,哪个有值用哪个:
- LibreHardwareMonitor(MSR 直读) —— 最准,但需要管理员 + 内核驱动能加载
- ACPI 热区兜底 —— 读性能计数器取所有热区里最高的那个,实测在 Windows 11 上不需要管理员权限
开发机上 LHM 即使在管理员下也拿不到 CPU 温度(WinRing0 类内核驱动没加载成功), 所以实际生效的是 ACPI 兜底:提权与不提权都能看到温度。
注意 ACPI 热区是主板/CPU 附近的热敏电阻,不等于核心温度(DIE 温度), 数值通常比
CPU Package略低、变化也更迟钝。
风扇转速为什么一直是空的
开发机(i9-11900H + RTX 3060 Laptop)根本没有对外暴露风扇传感器:
LHM 枚举不到任何 Fan 类型传感器,nvidia-smi --query-gpu=fan.speed 也返回 [N/A]。
功能本身是完整的 —— 在台式机或支持 SuperIO/EC 的机器上,只要传感器存在就会自动出现。
帧率与刷新率
- 刷新率:显示模式的真实值(如
60Hz),注意59通常就是 59.94Hz - 实时帧率:DXGI 桌面复制统计桌面合成器每秒真正呈现的画面数,按刷新率封顶
- 独占全屏游戏 / 远程串流会话下桌面复制会失效,此时该项自动隐藏
- 桌面完全静止时帧率显示
0,这是正常的;想一直看屏幕能力就用「刷新率」
配置文件
app\config.json(程序同目录,删掉就恢复默认)。主要项:
{
"RefreshMs": 1000, // 采样间隔(毫秒) 250~10000
"FontFamily": "Microsoft YaHei UI",
"FontSize": 11,
"Bold": true,
"TextColor": "#FFE9F1FF", // 数值颜色
"AccentColor": "#FF6FD3FF", // 名称高亮色
"BackColor": "#D2101319", // 背景色
"CornerRadius": 10,
"Opacity": 0.92, // 整体不透明度
"HoverFade": true, // 鼠标移入时强烈淡化
"HoverOpacity": 0.15, // 淡化后的不透明度(0 = 完全隐形)
"OffsetX": 0, // 相对屏幕顶部正中的偏移
"OffsetY": 0, // 0 = 完全紧贴屏幕上边缘
"ClickThrough": true, // 鼠标穿透
"HideUnavailable": true, // 自动隐藏读不到的数据
"NicId": "", // "" = 自动选流量最大的网卡;"*" = 全部合计
"Items": [ // 按顺序渲染,Line 0=第一行 1=第二行
{ "Key": "cpu.load", "Label": "CPU占用", "Template": "{label} {value}{unit}",
"Line": 0, "Enabled": true }
]
}
开机自启
设置 → 「位置与行为」→ 勾选开机自动启动。
- 提权运行时:创建计划任务(
ONLOGON+ 最高权限),开机自动以管理员启动,不再弹 UAC - 非提权运行时:退回到注册表
HKCU\...\Run - 取消勾选会同时清掉这两种
技术实现
- .NET 8 + WinForms,框架依赖部署
- LibreHardwareMonitorLib 0.9.6 —— CPU/GPU/主板传感器
- Vortice.Direct3D11 / Vortice.DXGI —— DXGI 桌面复制测帧率
- System.Management —— ACPI 热区温度兜底
- 内存:
GlobalMemoryStatusEx;网速:NetworkInterface.GetIPStatistics()差分 - 刷新率:
EnumDisplaySettings+GetDeviceCaps(VREFRESH) - 悬浮条:无边框
TopMost+WS_EX_TOOLWINDOW|WS_EX_NOACTIVATE,圆角 Region +Form.Opacity,WS_EX_TRANSPARENT实现鼠标穿透 - 采样在后台线程(默认 1Hz),UI 用 200ms 定时器读取最新快照并按需重绘
踩过的坑(改代码时注意)
- Vortice 的
Dispose()会把NativePointer清零,而同一 COM 对象可能被多处引用。 过早 Dispose 会让D3D11CreateDevice收到 NULL 适配器并返回E_INVALIDARG - 主显示器可能挂在虚拟显示适配器上,该适配器创建设备会失败,必须遍历所有 (适配器, 输出) 逐个回退
- 桌面复制的计数会超过面板刷新率(实测 79 > 60),所以要按刷新率封顶
- WinForms 里
TabPage.ClientSize在加入TabControl之前是无效值 .cmd必须用 CRLF;.ps1含中文必须存成 UTF-8 带 BOM(PowerShell 5.1 会把无 BOM 的按 GBK 解析)- 文件名里别放括号:
cmd /c 启动(管理员).cmd会在(处截断 WS_EX_TRANSPARENT必须写进CreateParams,不能只在建好窗口后SetWindowLong加一次 —— WinForms 每次重建句柄都会用CreateParams.ExStyle整体覆盖扩展样式,外部加的那一位会被静默抹掉- 别用「新配置对象 vs 旧配置对象」做差异比较来决定要不要重建资源 ——
设置界面应用后,多个组件可能持有同一个
AppConfig对象,自己跟自己比永远等于”没变化”
源码结构
SysMonitorBar\
build.cmd / build.ps1 编译(自动探测 .NET SDK)
launch.ps1 启动/退出逻辑(.cmd 只是它的外壳)
启动-管理员.cmd 提权启动
设置.cmd / 退出.cmd
src\SysMonitorBar\
Program.cs 入口 / 单实例 / 启动失败处理
AppConfig.cs 配置模型 + JSON 读写 + 日志
Metrics.cs 指标定义 / 指标目录 / 模板渲染
SensorService.cs 后台采样:LHM 枚举 + 内置指标自动匹配
DisplayFpsMonitor.cs DXGI 桌面复制帧率监测
ThermalZone.cs ACPI 热区温度兜底(免管理员)
NetMonitor.cs 网卡枚举与速率统计
Native.cs P/Invoke:内存 / 刷新率 / 窗口样式 / DPI
BarPainter.cs 悬浮条排版与绘制
OverlayForm.cs 顶部悬浮条窗口
SettingsForm.cs 设置界面
TrayApp.cs 托盘 + 协调
AutoStart.cs 开机自启 / 提权重启
命令行参数(一般用不到):
SysMonitorBar.exe --settings 启动后直接打开设置窗口
SysMonitorBar.exe --show 启动时强制显示悬浮条
SysMonitorBar.exe --hide 启动时强制隐藏悬浮条
日志:app\SysMonitorBar.log(有问题先看它)。
重新编译
双击 build.cmd 即可,需要 .NET 8 SDK(只装运行时不够)。它会按顺序自动找:
- 项目根目录下的
dotnet-path.txt(第一行写dotnet.exe完整路径) - 环境变量
SYSMONITORBAR_DOTNET PATH里的dotnet%ProgramFiles%\dotnet\dotnet.exe等常见位置%USERPROFILE%\.dotnet\dotnet.exe、%LOCALAPPDATA%\Microsoft\dotnet\dotnet.exe%DOTNET_ROOT%\dotnet.exe
SDK 装在非标准位置时,在项目根目录建 dotnet-path.txt,第一行写路径即可
(该文件已写进 .gitignore)。
build.cmd会先检查程序是否在运行 —— 运行中的程序会锁住app\里的文件,编译前得先退出。
常见问题
悬浮条不见了? 托盘图标右键 → 「显示 / 隐藏监控条」。
点不动 / 挡住我了? 默认就是鼠标穿透,不会挡。若真的挡住,按顺序排查:
-
是不是点过托盘菜单的「拖动调整位置」?该模式下穿透会被临时关闭,再点一次即可恢复
-
设置 →「位置与行为」→ 确认「鼠标穿透」是勾上的
-
还不行就查日志
app\SysMonitorBar.log,程序每次启动都会打印一行自检:[悬浮条] 创建完成 期望穿透=True 实际=True ExStyle=0x080901A8 OK看
实际是不是True、ExStyle里有没有0x20那一位
CPU 温度准吗? 优先用 LHM 的 MSR 直读;读不到时自动退回 ACPI 热区,后者是主板热敏电阻,通常比核心温度略低。
Q:帧率显示 0? 桌面完全静止时就是 0(没有新画面被呈现)。玩游戏 / 看视频时会动起来。独占全屏和远程桌面下测不了。
两个显示器,它跑到副屏去了? 设置 → 「位置与行为」→ 选「(主)」那块。现在按设备名保存,换显示器顺序也不会跑偏。