GetVolumeInformation通过dwVolumeFlags中的FILE_READ_ONLY_VOLUME标志可靠判断卷是否只读,需传入卷根路径(如"D:”),配合GetVolumePathName提取路径所在卷根,避免误用文件级属性或写入测试。

用 GetVolumeInformation 检查卷的文件系统特性
Windows 下无法直接通过路径“读取属性”判断是否为只读媒体(比如光盘或写保护 U 盘),因为 GetFileAttributes 对光盘根目录可能返回 FILE_ATTRIBUTE_READONLY,但对普通文件却常返回 0 —— 这个标志在光盘上不具有一致性,不能作为可靠依据。
真正有效的方式是获取该路径所在卷的底层信息,看它是否被标记为只读媒体。核心函数是 GetVolumeInformation,它会填充一个 dwVolumeFlags 参数,其中 FILE_READ_ONLY_VOLUME 标志就表示整个卷是只读的(典型如 CD-ROM、DVD-ROM、写保护 SD 卡等)。
- 必须传入卷根路径,例如
"D:\",而不是任意文件路径;可用PathStripToRoot或手动截取 - 调用前确保路径存在且可访问,否则会失败并返回 0
- 该标志由文件系统驱动在挂载时设置,不是靠用户权限或 ACL 推导,因此结果稳定
如何从任意文件路径提取卷根并调用 GetVolumeInformation
给定一个文件路径(如 "D:\data\config.txt"),需先归一化为卷根格式("D:\\"),再调用 API。注意结尾双反斜杠是 Windows 卷名的强制要求。
示例关键逻辑:
立即学习“C++免费学习笔记(深入)”;
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
// 假设 path = "D:\data\config.txt"
TCHAR volumeRoot[MAX_PATH];
if (GetVolumePathName(path, volumeRoot, MAX_PATH)) {
DWORD serial, maxCompLen, flags;
TCHAR fsName[256], volName[256];
if (GetVolumeInformation(volumeRoot, volName, _countof(volName),
&serial, &maxCompLen, &flags,
fsName, _countof(fsName))) {
if (flags & FILE_READ_ONLY_VOLUME) {
// 确认为只读媒体(光盘/写保护盘)
}
}
}
-
GetVolumePathName是安全提取卷根的首选,比字符串截断更健壮(兼容 UNC、长路径等) - 如果路径不存在或不可达,
GetVolumePathName会失败,此时不应默认为只读 - 某些虚拟光驱或特殊镜像工具可能不正确设置
FILE_READ_ONLY_VOLUME,但标准 Windows 光驱驱动和 USB 写保护控制器都会正确置位
不要依赖 GetFileAttributes 或写入测试
很多开发者尝试用 GetFileAttributes(path) & FILE_ATTRIBUTE_READONLY 判断,或者干脆创建临时文件来试探写入是否失败 —— 这两种方式都不可靠。
-
FILE_ATTRIBUTE_READONLY是文件级属性,光盘上所有文件默认带该标志,但 NTFS 上也可能人为设置,无法区分“媒体只读”和“文件只读” - 写入测试有副作用:可能触发 UAC 提权弹窗、占用资源、干扰防病毒软件,且在某些沙箱或受限环境中根本无法创建文件
- 即使写入失败,也可能是磁盘满、权限不足、路径非法等原因,不能反推为“只读媒体”
Linux/macOS 下没有等价的跨平台方案
C++ 标准库和 Boost 都不提供“判断是否为只读媒体”的抽象接口。POSIX 系统中,只读性由挂载选项决定(如 mount -o ro /dev/sr0 /mnt/cdrom),可通过解析 /proc/mounts(Linux)或 mount 命令输出匹配对应设备的 ro 字段来间接判断。
但这需要:
- 解析文本输出,易受 locale 和格式变更影响
- 需将路径映射到设备(如用
statfs获取f_fsid,再查/sys/block/),步骤繁琐 - macOS 需调用
getmntinfo+statfs,且光盘设备通常出现在/dev/diskX,但无统一只读标记字段
所以跨平台项目若真需此能力,Windows 用 GetVolumeInformation,其他系统建议明确限制使用场景,或降级为仅检查挂载选项(接受一定误判率)。
最常被忽略的一点:这个判断本质是卷级的,不是路径级的。哪怕你传入的是一个深层子目录,最终结论也只反映它所在的物理卷是否只读 —— 如果路径跨了符号链接跳到另一卷,GetVolumePathName 仍会返回原始卷,不会跟随链接。这点在调试时容易误以为逻辑出错。

















