deviceiocontrol是windows系统级函数,用于应用程序通过ioctl码与设备驱动通信,支持同步/异步调用,需配合createfile获取设备句柄,并通过ctl_code宏定义控制码,常用于磁盘管理、驱动配置等场景。

如何用 Windows API 获取文件所在卷的物理设备类型
Windows 不直接暴露“这个路径在 NVMe 盘上”这种语义,但可以通过 GetVolumePathName → GetVolumeNameForVolumeMountPoint → CreateFile 打开卷设备 → DeviceIoControl 查询 IOCTL_STORAGE_QUERY_PROPERTY 获取总线类型来判断。关键不是看盘符名或路径字符串,而是查底层存储总线。
常见错误是只检查 GetDriveType 返回 DRIVE_FIXED 就认为是 SSD/NVMe——这完全不可靠,SATA SSD、SAS 盘、甚至某些 USB-C 接口的 NVMe 移动盘都可能返回相同值。
- 必须先从文件路径提取卷根,例如
"C:\data\file.txt"→ 卷路径为"C:\" - 调用
GetVolumePathName确保路径合法,再用GetVolumeNameForVolumeMountPoint得到类似"\\?\Volume{...}\"的格式 - 拼接
"\\?\" + volume_guid + "PhysicalDrive0"(注意:此处的 0 是逻辑序号,需通过IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS映射到真实PhysicalDriveX)
为什么不能直接用 PhysicalDrive0 硬编码序号
一个卷可能跨多个物理磁盘(如跨区卷、存储池),也可能一个物理盘被划分为多个卷。硬写 PhysicalDrive0 极大概率打开错设备,导致 ERROR_ACCESS_DENIED 或查询结果无效。
正确做法是调用 DeviceIoControl 向卷句柄发送 IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,解析返回的 VOLUME_DISK_EXTENTS 结构体,从中拿到 Extents[0].DiskNumber,再构造 "\?PhysicalDrive%d" 路径。
-
IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS必须用GENERIC_READ权限打开卷句柄(非管理员也能成功) - 返回的
DiskNumber是整数,不是字符串,别拼错成"PhysicalDrive" + std::to_string(n)这种易出错写法 - 如果
NumberOfExtents > 1,说明该卷跨盘,此时无法简单归类为“NVMe”,应视为混合类型
用 IOCTL_STORAGE_QUERY_PROPERTY 判断 NVMe 总线类型
打开 \\?\PhysicalDriveX 后,向其发送 IOCTL_STORAGE_QUERY_PROPERTY,设置 PropertyId = StorageAdapterProperty,然后检查返回结构体 STORAGE_ADAPTER_DESCRIPTOR 中的 BusType 字段。
NVMe 设备的 BusType 值为 BusTypeNVMe(定义在 winioctl.h,值为 18)。注意:Windows 10 1607+ 才支持该枚举值,旧系统会返回 BusTypeUnknown 或直接失败。
- 必须用
FILE_SHARE_READ | FILE_SHARE_WRITE打开PhysicalDriveX,否则多数情况下会失败 - 调用前需确保已启用
SE_MANAGE_VOLUME_NAME权限(普通用户默认有,但某些受限制环境可能被禁用) - 若
DeviceIoControl返回 false 且GetLastError()为ERROR_INSUFFICIENT_BUFFER,说明输出缓冲区太小,需重试并增大 buffer
实际使用中容易被忽略的权限与兼容性细节
整个链路对权限和系统版本敏感,不是所有机器都能跑通。最常卡在两处:一是 UAC 提权缺失导致 CreateFile 对 PhysicalDriveX 失败;二是 Windows 7 或 Server 2008 R2 不支持 BusTypeNVMe,查出来永远是 BusTypeUnknown。
- 不需要管理员权限就能完成全部流程——前提是程序未被沙盒化(如 Microsoft Store 应用)、且目标盘未被 BitLocker 或 VSS 锁定
- 若目标路径在 ReFS 卷、Storage Spaces 池、或 WSL2 的虚拟磁盘上,
BusType可能返回BusTypeUnknown或BusTypeRAID,这类情况只能退而求其次查 SMART 信息或厂商 ID 字符串 - 某些 OEM NVMe 盘固件会故意把
BusType报告为BusTypeRAID(尤其戴尔/惠普服务器),此时需额外读取STORAGE_DEVICE_DESCRIPTOR中的VendorId和ProductId做白名单匹配
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











