You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Windows下替代Linux /dev/disk/by-path/的磁盘端口识别C++实现问询

Windows下通过端口位置识别磁盘的C++实现方案

在Windows中,要实现类似Linux /dev/disk/by-path/ 的磁盘端口位置识别,需借助SetupAPI和存储设备IO控制API,直接枚举底层磁盘设备(无需依赖分区或盘符),核心是获取设备的硬件路径与端口属性信息。

核心实现思路

Windows设备管理器的“按连接查看”基于设备的硬件位置路径和总线属性,通过以下步骤可在C++中实现:

  1. 枚举所有磁盘设备(包括无分区/未识别文件系统的磁盘)
  2. 读取设备的硬件位置路径(包含总线、端口编号等信息)
  3. 查询存储设备的总线类型、端口号等属性,关联磁盘与端口位置
  4. 可选:监听设备增删事件,实时更新磁盘列表

具体步骤与代码示例

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 04:20:31