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

如何通过设备实例路径获取USB设备的位置路径属性

解决方案:通过SetupAPI映射设备实例路径到位置路径

要解决设备实例路径到位置路径的映射问题,直接使用Windows SetupAPI是最可靠的方案——它能直接读取设备管理器中显示的位置路径信息,避开注册表和WMI的局限性。以下是C#的具体实现步骤:

核心思路

通过SetupAPI的一系列函数枚举系统中所有设备,匹配目标设备的实例路径后,读取其DEVPKEY_Device_LocationPaths属性,该属性对应的就是设备管理器中显示的位置路径字符串。

C#实现代码

首先引入必要的P/Invoke声明,再编写匹配逻辑:

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;

public class DeviceLocationHelper
{
    // SetupAPI 函数声明
    [DllImport("setupapi.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern IntPtr SetupDiGetClassDevs(ref Guid ClassGuid, string Enumerator, IntPtr hwndParent, uint Flags);

    [DllImport("setupapi.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern bool SetupDiEnumDeviceInfo(IntPtr DeviceInfoSet, uint MemberIndex, ref SP_DEVINFO_DATA DeviceInfoData);

    [DllImport("setupapi.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern bool SetupDiGetDeviceRegistryProperty(IntPtr DeviceInfoSet, ref SP_DEVINFO_DATA DeviceInfoData, uint Property, out uint PropertyRegDataType, IntPtr PropertyBuffer, uint PropertyBufferSize, out uint RequiredSize);

    [DllImport("setupapi.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern bool SetupDiGetDeviceRegistryProperty(IntPtr DeviceInfoSet, ref SP_DEVINFO_DATA DeviceInfoData, Guid PropertyGuid, uint PropertyPid, out uint PropertyRegDataType, IntPtr PropertyBuffer, uint PropertyBufferSize, out uint RequiredSize);

    [DllImport("setupapi.dll", SetLastError = true)]
    private static extern bool SetupDiDestroyDeviceInfoList(IntPtr DeviceInfoSet);

    // 结构体定义
    [StructLayout(LayoutKind.Sequential)]
    private struct SP_DEVINFO_DATA
    {
        public uint cbSize;
        public Guid ClassGuid;
        public uint DevInst;
        public IntPtr Reserved;
    }

    // 属性常量
    private const uint DIGCF_ALLCLASSES = 0x00000004;
    private const uint SPDRP_DEVICE_INSTANCE_PATH = 0x0000001B;
    private static readonly Guid DEVPKEY_Device_LocationPaths_Fmtid = new Guid("78C34FC8-104A-4ACA-9EA4-524D52996E57");
    private const uint DEVPKEY_Device_LocationPaths_Pid = 3;

    /// <summary>
    /// 根据设备实例路径获取对应的位置路径
    /// </summary>
    /// <param name="deviceInstancePath">设备实例路径,如USB\VID_0AB4&PID_0010\IG011556</param>
    /// <returns>位置路径列表,如[PCIROOT(0)#PCI(1D04)#USBROOT(0)#USB(4)]</returns>
    public static List<string> GetDeviceLocationPaths(string deviceInstancePath)
    {
        List<string> locationPaths = new List<string>();
        Guid allClassesGuid = Guid.Empty;
        IntPtr deviceInfoSet = IntPtr.Zero;

        try
        {
            // 获取所有设备的信息集合
            deviceInfoSet = SetupDiGetClassDevs(ref allClassesGuid, null, IntPtr.Zero, DIGCF_ALLCLASSES);
            if (deviceInfoSet == IntPtr.Zero)
                throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());

            SP_DEVINFO_DATA deviceInfoData = new SP_DEVINFO_DATA();
            deviceInfoData.cbSize = (uint)Marshal.SizeOf(deviceInfoData);

            // 枚举每个设备
            for (uint i = 0; SetupDiEnumDeviceInfo(deviceInfoSet, i, ref deviceInfoData); i++)
            {
                // 获取当前设备的实例路径
                string currentInstancePath = GetDevicePropertyById(deviceInfoSet, ref deviceInfoData, SPDRP_DEVICE_INSTANCE_PATH);
                if (string.Equals(currentInstancePath, deviceInstancePath, StringComparison.OrdinalIgnoreCase))
                {
                    // 获取位置路径
                    string paths = GetDevicePropertyByDevPKey(deviceInfoSet, ref deviceInfoData);
                    if (!string.IsNullOrEmpty(paths))
                    {
                        // 位置路径是多字符串格式,按\0分割
                        string[] splitPaths = paths.Split(new[] { '\0' }, StringSplitOptions.RemoveEmptyEntries);
                        locationPaths.AddRange(splitPaths);
                    }
                    break;
                }

                // 重置结构体大小,避免下一次枚举出错
                deviceInfoData.cbSize = (uint)Marshal.SizeOf(deviceInfoData);
            }

            int lastError = Marshal.GetLastWin32Error();
            if (lastError != 0 && lastError != 259) // 259是枚举结束的正常错误码
                throw new System.ComponentModel.Win32Exception(lastError);
        }
        finally
        {
            // 释放资源
            if (deviceInfoSet != IntPtr.Zero)
                SetupDiDestroyDeviceInfoList(deviceInfoSet);
        }

        return locationPaths;
    }

    private static string GetDevicePropertyById(IntPtr deviceInfoSet, ref SP_DEVINFO_DATA deviceInfoData, uint propertyId)
    {
        uint regDataType;
        uint requiredSize = 0;

        // 先获取所需缓冲区大小
        if (!SetupDiGetDeviceRegistryProperty(deviceInfoSet, ref deviceInfoData, propertyId, out regDataType, IntPtr.Zero, 0, out requiredSize))
        {
            if (Marshal.GetLastWin32Error() != 122) // 122是缓冲区不足的正常错误码
                return null;
        }

        IntPtr propertyBuffer = Marshal.AllocHGlobal((int)requiredSize);
        try
        {
            if (SetupDiGetDeviceRegistryProperty(deviceInfoSet, ref deviceInfoData, propertyId, out regDataType, propertyBuffer, requiredSize, out requiredSize))
            {
                if (regDataType == 1) // REG_SZ 或 REG_MULTI_SZ
                    return Marshal.PtrToStringUni(propertyBuffer);
            }
            return null;
        }
        finally
        {
            Marshal.FreeHGlobal(propertyBuffer);
        }
    }

    private static string GetDevicePropertyByDevPKey(IntPtr deviceInfoSet, ref SP_DEVINFO_DATA deviceInfoData)
    {
        uint regDataType;
        uint requiredSize = 0;

        // 使用SetupDiGetDeviceRegistryProperty的重载获取DEVPKEY属性
        if (!SetupDiGetDeviceRegistryProperty(deviceInfoSet, ref deviceInfoData, DEVPKEY_Device_LocationPaths_Fmtid, DEVPKEY_Device_LocationPaths_Pid, out regDataType, IntPtr.Zero, 0, out requiredSize))
        {
            if (Marshal.GetLastWin32Error() != 122)
                return null;
        }

        IntPtr propertyBuffer = Marshal.AllocHGlobal((int)requiredSize);
        try
        {
            if (SetupDiGetDeviceRegistryProperty(deviceInfoSet, ref deviceInfoData, DEVPKEY_Device_LocationPaths_Fmtid, DEVPKEY_Device_LocationPaths_Pid, out regDataType, propertyBuffer, requiredSize, out requiredSize))
            {
                if (regDataType == 1)
                    return Marshal.PtrToStringUni(propertyBuffer);
            }
            return null;
        }
        finally
        {
            Marshal.FreeHGlobal(propertyBuffer);
        }
    }
}

// 调用示例
class Program
{
    static void Main(string[] args)
    {
        string deviceInstancePath = "USB\\VID_0AB4&PID_0010\\IG011556";
        List<string> locationPaths = DeviceLocationHelper.GetDeviceLocationPaths(deviceInstancePath);

        foreach (string path in locationPaths)
        {
            Console.WriteLine("位置路径: " + path);
        }
    }
}

关键说明

  1. 权限要求:程序需要以管理员权限运行,否则SetupAPI可能无法访问部分设备的属性信息。
  2. 系统兼容性:DEVPKEY_Device_LocationPaths属性从Windows Vista开始支持,确保运行环境符合要求。
  3. 多路径处理:部分设备可能有多个位置路径(比如同时支持USB和其他接口),代码中会返回所有路径,可根据实际需求选择合适的条目。
  4. 错误处理:代码中捕获了Win32错误码,便于调试排查问题。

内容的提问来源于stack exchange,提问作者SourceC0de

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 20:50:54