windows 中卷序列号是格式化生成的32位十六进制值,通过wmi win32_volume获取;linux无统一磁盘序列号,硬件s/n常为空,推荐用文件系统uuid。

Windows 下用 WMI 查询 Win32_Volume 获取卷序列号
磁盘“序列号”在 Windows 中实际对应的是卷的序列号(Volume Serial Number),不是物理硬盘的 S/N。它由格式化时生成,是 32 位十六进制值(如 0x12a3b4c5),可通过 WMI 的 Win32_Volume 类获取,比过时的 GetVolumeInformation 更可靠(尤其对 NTFS 卷、挂载点、ReFS 支持更好)。
实操要点:
- 需链接
comctl32.lib和wbemuuid.lib,初始化 COM 和 WMI 服务 - 查询语句用
"SELECT Name, SerialNumber FROM Win32_Volume WHERE DriveLetter = 'C:'"(注意单引号) -
SerialNumber字段返回的是字符串形式的十进制数(如"312897733"),需用std::stoul(str, nullptr, 10)转为整数再转十六进制才匹配命令行vol C:输出 - 若查
DriveLetter为空的卷(如挂载点),改用DeviceID过滤,例如"\\?\Volume{...}"
Linux 下读取 /sys/block/*/device/serial 或用 udevadm
Linux 没有统一“磁盘序列号”概念:SATA/NVMe 设备的序列号由硬件提供,但常被厂商设为全空或占位符;而文件系统层(如 ext4)的 UUID 或卷标才是更实用的唯一标识。
实操要点:
- 尝试读取
/sys/block/sda/device/serial—— 大部分 SATA 盘支持,但 NVMe 盘通常该路径不存在或为空 - 用命令
udevadm info --name=/dev/sda --query=property | grep ID_SERIAL可合并多个来源(ID_SERIAL,ID_SERIAL_SHORT),C++ 中可用popen()调用并解析输出 - 注意权限:非 root 用户可能无法读取
/sys/block/*/device/serial,需加 udev 规则或改用用户可读的ID_WWN(如有) - 不要依赖
/dev/disk/by-id/ata-*链接名——它本质是序列号的转义,但某些固件会动态变更
C++ 跨平台封装建议:按平台分发逻辑,不强行统一接口
试图写一个“跨平台获取磁盘序列号”的通用函数容易踩坑,因为 Windows 的 Volume Serial Number 和 Linux 的设备序列号语义不同、稳定性不同、获取成本也不同。
推荐做法:
- Windows 端直接走 WMI(不用
GetVolumeInformation,后者对 ReFS 或无盘符卷支持差) - Linux 端优先返回
blkid -o export /dev/sda1 | grep UUID得到的UUID(文件系统级,稳定且无需 root) - 若业务真需要硬件序列号,Linux 上 fallback 到
smartctl -i /dev/sda(需安装 smartmontools 并处理 sudo 权限) - 所有路径和命令字符串必须用
std::string拼接,避免硬编码空格或引号引发 shell 解析错误
常见错误:GetVolumeInformation 返回 0 或乱码
这是最常被误用的 API。它返回的 lpVolumeSerialNumber 是指卷序列号,但调用失败时不会报错,只返回 0,且不检查路径有效性。
典型问题:
- 传入路径如
"C:"不带反斜杠 —— 必须是"C:\"或"C:\temp"(任意存在子目录) - 目标路径在符号链接或 WSL2 挂载点下,API 可能返回 0 或无效值
- 未检查返回值:必须判断函数返回
TRUE,否则lpVolumeSerialNumber内容未定义 - 忽略大小端:Windows 返回的是小端 DWORD,直接 printf("%x") 即可,无需手动翻转字节
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











