windows 用 wmi(win32_battery,rootcimv2)最可靠;linux 优先读 /sys/class/power_supply/ 下动态路径;macos 必须用 iokit(applesmartbattery)并申请全盘访问权限;跨平台应分实现、统一结构体、不缓存、妥善处理无电池场景。

Windows 下用 WMI 查询电池状态最可靠
直接调用 WMI 是 Windows 平台读取电池信息最稳定的方式,绕过驱动层兼容性问题,也不依赖 GetSystemPowerStatus 这类只返回粗略状态的旧 API。
常见错误是硬编码 WMI 类名或忽略权限——WMI 查询需要启用 COM 初始化和适当的安全上下文,否则 CoInitializeEx 后调用 ConnectServer 会静默失败或返回 WBEM_E_ACCESS_DENIED。
- 必须在调用前执行
CoInitializeEx(nullptr, COINIT_MULTITHREADED),单线程公寓(COINIT_APARTMENTTHREADED)也可,但不能省略 - 连接命名空间要用
ROOT\CIMV2,不是ROOT\WMI(后者部分电池字段不全) - 查电池用
Win32_Battery类,注意它可能返回空结果:笔记本没插电池、驱动未加载、或设备是台式机(此时 WMI 实例数为 0) - 关键字段包括
EstimatedChargeRemaining(0–100)、BatteryStatus(1=正常,2=低电,10=充电中)、EstimatedRunTime(分钟,仅放电时有效)
Linux 下优先读取 /sys/class/power_supply/ 文件系统
Linux 没有统一 API,ACPI 层信息通过 sysfs 暴露,这是内核标准行为,比调用 upower D-Bus 接口更轻量、无依赖、无需特权。
容易踩的坑是路径硬编码——不同设备电池目录名不同:BAT0、bat0、axp288_fuel_gauge 都可能出现;且部分嵌入式设备甚至没有 capacity 文件,只有 energy_now/energy_full。
立即学习“C++免费学习笔记(深入)”;
- 先遍历
/sys/class/power_supply/下所有子目录,过滤出含online和capacity文件的项(排除 USB/AC 适配器目录) - 读
status文件判断是否在充电(值为Charging/Discharging/Full) -
capacity是整数百分比,但某些老内核返回Unknown,需做字符串校验 - 避免轮询太频繁:内核通常每 30 秒更新一次,1 秒读一次纯属浪费 fd 和磁盘 I/O
macOS 上只能走 IOKit,没捷径
Apple 不开放 ACPI 接口,WMI 更不存在,唯一合法途径是 IOKit 框架里的 IOServiceGetMatchingServices 查找 AppleSmartBattery 实例。
难点不在代码长度,而在权限和符号链接:从 macOS 10.15(Catalina)起,即使命令行工具也需 Full Disk Access 权限才能读取电池数据,否则 IORegistryEntryCreateCFProperty 返回 nullptr 且无错误提示。
- 必须链接
-framework IOKit -framework CoreFoundation - 匹配字典用
IOServiceMatching("AppleSmartBattery"),别写错大小写 - 关键属性名是
"CurrentCapacity"、"MaxCapacity"、"ExternalConnected"、"FullyCharged",注意不是驼峰也不是下划线 - 调试时用
ioreg -l | grep -i "battery|capacity"确认实际键名,有些机型返回的是"DesignCapacity"
跨平台封装要注意三件事
如果写通用库,别试图抽象出一个“统一 Battery 类”——各系统字段语义差异大:Linux 的 capacity 是瞬时值,Windows 的 EstimatedChargeRemaining 是预测值,macOS 的 CurrentCapacity 是绝对毫瓦时,单位都不一致。
最容易被忽略的是“无电池”场景的处理:台式机、某些二合一设备拆掉键盘底座后,Linux 可能只剩 AC 供电目录,Windows 的 Win32_Battery 实例数为 0,macOS 根本找不到 AppleSmartBattery。这时候返回 “unknown” 比强行返回 100% 更诚实。
- 每个平台单独实现获取逻辑,共用一个结构体(如
struct BatteryInfo { int percent; bool charging; int remaining_minutes; }),缺失字段填 -1 - 不要缓存结果——电池状态变化快,每次调用都应重新读取
- Windows 和 macOS 需要额外链接库(
wbemuuid.lib/IOKit.framework),CMake 或 Makefile 里漏掉就直接链接失败










