Windows下替代Linux /dev/disk/by-path/的磁盘端口识别C++实现问询
Windows下通过端口位置识别磁盘的C++实现方案
在Windows中,要实现类似Linux /dev/disk/by-path/ 的磁盘端口位置识别,需借助SetupAPI和存储设备IO控制API,直接枚举底层磁盘设备(无需依赖分区或盘符),核心是获取设备的硬件路径与端口属性信息。
核心实现思路
Windows设备管理器的“按连接查看”基于设备的硬件位置路径和总线属性,通过以下步骤可在C++中实现:
- 枚举所有磁盘设备(包括无分区/未识别文件系统的磁盘)
- 读取设备的硬件位置路径(包含总线、端口编号等信息)
- 查询存储设备的总线类型、端口号等属性,关联磁盘与端口位置
- 可选:监听设备增删事件,实时更新磁盘列表
具体步骤与代码示例
1. 枚举磁盘设备
使用SetupDiGetClassDevs指定磁盘设备接口类(GUID_DEVINTERFACE_DISK),遍历所有已连接的磁盘设备:
#include <windows.h> #include <setupapi.h> #include <initguid.h> #include <devguid.h> #include <ntdddisk.h> #include <ntddstor.h> #include <iostream> #include <vector> #include <string> // 磁盘设备接口GUID DEFINE_GUID(GUID_DEVINTERFACE_DISK, 0x53f56307L, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b); void EnumerateDisksWithPortInfo() { HDEVINFO hDevInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_DISK, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); if (hDevInfo == INVALID_HANDLE_VALUE) { std::cerr << "SetupDiGetClassDevs failed: " << GetLastError() << std::endl; return; } SP_DEVICE_INTERFACE_DATA deviceInterfaceData = {0}; deviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); for (DWORD idx = 0; SetupDiEnumDeviceInterfaces(hDevInfo, NULL, &GUID_DEVINTERFACE_DISK, idx, &deviceInterfaceData); idx++) { // 获取设备详情所需的缓冲区大小 DWORD requiredSize = 0; SetupDiGetDeviceInterfaceDetail(hDevInfo, &deviceInterfaceData, NULL, 0, &requiredSize, NULL); if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) continue; // 分配缓冲区并获取设备详情 std::vector<BYTE> detailBuffer(requiredSize); PSP_DEVICE_INTERFACE_DETAIL_DATA pDetailData = reinterpret_cast<PSP_DEVICE_INTERFACE_DETAIL_DATA>(detailBuffer.data()); pDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); SP_DEVINFO_DATA devInfoData = {0}; devInfoData.cbSize = sizeof(SP_DEVINFO_DATA); if (!SetupDiGetDeviceInterfaceDetail(hDevInfo, &deviceInterfaceData, pDetailData, requiredSize, NULL, &devInfoData)) { std::cerr << "SetupDiGetDeviceInterfaceDetail failed: " << GetLastError() << std::endl; continue; } // 1. 获取设备位置路径(包含端口信息) TCHAR locationPath[1024] = {0}; if (SetupDiGetDeviceRegistryProperty(hDevInfo, &devInfoData, SPDRP_LOCATION_PATHS, NULL, reinterpret_cast<PBYTE>(locationPath), sizeof(locationPath), NULL)) { std::wcout << L"磁盘设备路径: " << pDetailData->DevicePath << std::endl; std::wcout << L"硬件位置路径: " << locationPath << std::endl; } // 2. 查询存储设备端口属性 HANDLE hDisk = CreateFile(pDetailData->DevicePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (hDisk != INVALID_HANDLE_VALUE) { STORAGE_PROPERTY_QUERY query = {0}; query.PropertyId = StorageAdapterProperty; query.QueryType = PropertyStandardQuery; STORAGE_ADAPTER_DESCRIPTOR adapterDesc = {0}; DWORD bytesReturned = 0; if (DeviceIoControl(hDisk, IOCTL_STORAGE_QUERY_PROPERTY, &query, sizeof(query), &adapterDesc, sizeof(adapterDesc), &bytesReturned, NULL)) { std::cout << "总线类型: " << adapterDesc.BusType << std::endl; std::cout << "端口编号: " << adapterDesc.PortNumber << std::endl; } CloseHandle(hDisk); } std::cout << "-------------------------" << std::endl; } SetupDiDestroyDeviceInfoList(hDevInfo); } int main() { EnumerateDisksWithPortInfo(); return 0; }
2. 解析端口信息
- 硬件位置路径(
SPDRP_LOCATION_PATHS):格式类似PCIROOT(0)#PCI(1F02)#ATA(C00T0L0),其中C00T0L0代表控制器、目标、逻辑单元号,对应物理端口位置;USB设备路径会包含USBROOT(0)#USB(1)这类端口编号。 - STORAGE_ADAPTER_DESCRIPTOR:通过
IOCTL_STORAGE_QUERY_PROPERTY获取的PortNumber字段直接对应磁盘连接的端口序号,结合BusType(如BusTypeUsb、BusTypeSata)可明确端口类型。
3. 监听设备增删事件
若需实时感知磁盘增删,可通过RegisterDeviceNotification注册窗口消息监听,当WM_DEVICECHANGE消息触发时,重新枚举磁盘设备即可更新列表。
关键注意事项
- 权限要求:程序需以管理员权限运行,否则无法枚举底层设备或读取注册表属性。
- 跨总线兼容:不同总线(SATA/USB/NVMe)的位置路径格式不同,需根据
BusType做针对性解析。 - 无分区磁盘兼容:此方案直接枚举磁盘设备对象,无需依赖分区或盘符,即使磁盘未格式化也能识别。
内容的提问来源于stack exchange,提问作者Glenn Coombs
相关产品推荐
相关产品推荐

