从 VID/PID 中查找 USB 驱动器号(XP 和更高版本需要)

Posted

技术标签:

【中文标题】从 VID/PID 中查找 USB 驱动器号(XP 和更高版本需要)【英文标题】:Find USB drive letter from VID/PID (Needed for XP and higher) 【发布时间】:2013-06-26 15:10:56 【问题描述】:

所以我想我会在此处包含最终答案,这样您就不必理解这篇文章了。非常感谢 Simon Mourier 花时间解决这个问题。

我的工作代码

        try
        
            //Get a list of available devices attached to the USB hub
            List<string> disks = new List<string>();
            var usbDevices = GetUSBDevices();

            //Enumerate the USB devices to see if any have specific VID/PID
            foreach (var usbDevice in usbDevices)
            
                if (usbDevice.DeviceID.Contains(USB_PID) && usbDevice.DeviceID.Contains(USB_VID))
                
                    foreach (string name in usbDevice.GetDiskNames())
                    
                        //Open dialog to show file names
                        textbox1.Text = name.ToString();
                    
                                   
            

所以只需使用我原始问题中的GetUSBDevices,然后包括 Simon Mourier 的回答中显示的两个类,这应该很好!


原始问题

我知道以前有人问过这个问题(请参阅here),但他们都没有确定的答案,我已经尝试了所有建议的答案。不幸的是,这些线程早已死去,我希望有人能在这里给出更好的答案。

到目前为止,我有两个“起点”,我将在下面展示每个起点。


选项 1:(获取 VID/PID 但不获取驱动器号)

我有一个通过应用程序连接到的嵌入式设备。我有成功扫描任何 USB 设备并检查 VID/PID 的代码。我成功检测到我的设备,但我不知道如何获取驱动器号。有人可以帮我吗?我觉得我可以在 class 中添加另一行,但是当我通过 Device Manager 时,我找不到任何描述驱动器号的属性。

谢谢!

我将在下面包含我的代码。

private void tsDownload_Click(object sender, EventArgs e)
    
        var usbDevices = GetUSBDevices();

        foreach (var usbDevice in usbDevices)
        
            if (usbDevice.DeviceID.Contains(USB_PID) && usbDevice.DeviceID.Contains(USB_VID))
                                
                //Find drive letter here
            
        
    

这些函数在哪里:

 static List<USBDeviceInfo> GetUSBDevices()
    
      List<USBDeviceInfo> devices = new List<USBDeviceInfo>();

      ManagementObjectCollection collection;
      using (var searcher = new ManagementObjectSearcher(@"Select * From Win32_USBHub"))
        collection = searcher.Get();      

      foreach (var device in collection)
      
        devices.Add(new USBDeviceInfo(
        (string)device.GetPropertyValue("DeviceID"),
        (string)device.GetPropertyValue("PNPDeviceID"),
        (string)device.GetPropertyValue("Description")            
        ));
      

      collection.Dispose();
      return devices;
          

班级是:

class USBDeviceInfo

    public USBDeviceInfo(string deviceID, string pnpDeviceID, string description)
    
      this.DeviceID = deviceID;
      this.PnpDeviceID = pnpDeviceID;
      this.Description = description;
    
    public string DeviceID  get; private set; 
    public string PnpDeviceID  get; private set; 
    public string Description  get; private set; 


选项 2:(获取驱动器号,但不获取 VID/PID)

foreach (ManagementObject drive in new ManagementObjectSearcher("select * from Win32_DiskDrive where InterfaceType='USB'").Get())
            
                foreach(ManagementObject partition in new ManagementObjectSearcher("ASSOCIATORS OF Win32_DiskDrive.DeviceID='" + drive["DeviceID"] + "' WHERE AssocClass = Win32_DiskDriveToDiskPartition").Get())
                
                    foreach (ManagementObject disk in new ManagementObjectSearcher("ASSOCIATORS OF Win32_DiskPartition.DeviceID='" + partition["DeviceID"] + "' WHERE AssocClass = Win32_LogicalDiskToPartition").Get())
                    
                        textBox1.Text = disk["Name"].ToString();

                    
                
            

我猜 VID/PID 位于 disk 对象属性之一中,但我找不到哪个。

【问题讨论】:

+1 用于研究工作。我希望你能得到你正在寻找的答案。我也很好奇。 我认为这种混淆/困难来自于并非每个设备都有驱动器号的事实,即使这样,一个设备也可以有很多驱动器号。只是我的新手理解...... 我已经连接了两个 USB 智能卡读卡器,但是当我运行 OPTION 2 示例时,我没有在“ManagementObjectSearcher”中连接任何 USB。 【参考方案1】:

我可能弄错了,但 WMI 似乎不知道 Windows 设备设置 API 中存在的父子关系。

所以,我创建了一个小的 Device 实用程序类,它可以从本机 Setup API 添加这个缺失的链接。以下是您在原来的 USBDeviceInfo 类中使用它的方法:

class USBDeviceInfo

    public USBDeviceInfo(string deviceID, string pnpDeviceID, string description)
    
        this.DeviceID = deviceID;
        this.PnpDeviceID = pnpDeviceID;
        this.Description = description;
    

    public string DeviceID  get; private set; 
    public string PnpDeviceID  get; private set; 
    public string Description  get; private set; 

    public IEnumerable<string> GetDiskNames()
    
        using (Device device = Device.Get(PnpDeviceID))
        
            // get children devices
            foreach (string childDeviceId in device.ChildrenPnpDeviceIds)
            
                // get the drive object that correspond to this id (escape the id)
                foreach (ManagementObject drive in new ManagementObjectSearcher("SELECT DeviceID FROM Win32_DiskDrive WHERE PNPDeviceID='" + childDeviceId.Replace(@"\", @"\\") + "'").Get())
                
                    // associate physical disks with partitions
                    foreach (ManagementObject partition in new ManagementObjectSearcher("ASSOCIATORS OF Win32_DiskDrive.DeviceID='" + drive["DeviceID"] + "' WHERE AssocClass=Win32_DiskDriveToDiskPartition").Get())
                    
                        // associate partitions with logical disks (drive letter volumes)
                        foreach (ManagementObject disk in new ManagementObjectSearcher("ASSOCIATORS OF Win32_DiskPartition.DeviceID='" + partition["DeviceID"] + "' WHERE AssocClass=Win32_LogicalDiskToPartition").Get())
                        
                            yield return (string)disk["DeviceID"];
                        
                    
                
            
        
    

这是新的设备类:

public sealed class Device : IDisposable

    private IntPtr _hDevInfo;
    private SP_DEVINFO_DATA _data;

    private Device(IntPtr hDevInfo, SP_DEVINFO_DATA data)
    
        _hDevInfo = hDevInfo;
        _data = data;
    

    public static Device Get(string pnpDeviceId)
    
        if (pnpDeviceId == null)
            throw new ArgumentNullException("pnpDeviceId");

        IntPtr hDevInfo = SetupDiGetClassDevs(IntPtr.Zero, pnpDeviceId, IntPtr.Zero, DIGCF.DIGCF_ALLCLASSES | DIGCF.DIGCF_DEVICEINTERFACE);
        if (hDevInfo == (IntPtr)INVALID_HANDLE_VALUE)
            throw new Win32Exception(Marshal.GetLastWin32Error());

        SP_DEVINFO_DATA data = new SP_DEVINFO_DATA();
        data.cbSize = Marshal.SizeOf(data);
        if (!SetupDiEnumDeviceInfo(hDevInfo, 0, ref data))
        
            int err = Marshal.GetLastWin32Error();
            if (err == ERROR_NO_MORE_ITEMS)
                return null;

            throw new Win32Exception(err);
        

        return new Device(hDevInfo, data) PnpDeviceId = pnpDeviceId;
    

    public void Dispose()
    
        if (_hDevInfo != IntPtr.Zero)
        
            SetupDiDestroyDeviceInfoList(_hDevInfo);
            _hDevInfo = IntPtr.Zero;
        
    

    public string PnpDeviceId  get; private set; 

    public string ParentPnpDeviceId
    
        get
        
            if (IsVistaOrHiger)
                return GetStringProperty(DEVPROPKEY.DEVPKEY_Device_Parent);

            uint parent;
            int cr = CM_Get_Parent(out parent, _data.DevInst, 0);
            if (cr != 0)
                throw new Exception("CM Error:" + cr);

            return GetDeviceId(parent);
        
    

    private static string GetDeviceId(uint inst)
    
        IntPtr buffer = Marshal.AllocHGlobal(MAX_DEVICE_ID_LEN + 1);
        int cr = CM_Get_Device_ID(inst, buffer, MAX_DEVICE_ID_LEN + 1, 0);
        if (cr != 0)
            throw new Exception("CM Error:" + cr);

        try
        
            return Marshal.PtrToStringAnsi(buffer);
        
        finally
        
            Marshal.FreeHGlobal(buffer);
        
    

    public string[] ChildrenPnpDeviceIds
    
        get
        
            if (IsVistaOrHiger)
                return GetStringListProperty(DEVPROPKEY.DEVPKEY_Device_Children);

            uint child;
            int cr = CM_Get_Child(out child, _data.DevInst, 0);
            if (cr != 0)
                return new string[0];

            List<string> ids = new List<string>();
            ids.Add(GetDeviceId(child));
            do
            
                cr = CM_Get_Sibling(out child, child, 0);
                if (cr != 0)
                    return ids.ToArray();

                ids.Add(GetDeviceId(child));
            
            while (true);
        
    

    private static bool IsVistaOrHiger
    
        get
        
            return (Environment.OSVersion.Platform == PlatformID.Win32NT && Environment.OSVersion.Version.CompareTo(new Version(6, 0)) >= 0);
        
    

    private const int INVALID_HANDLE_VALUE = -1;
    private const int ERROR_NO_MORE_ITEMS = 259;
    private const int MAX_DEVICE_ID_LEN = 200;

    [StructLayout(LayoutKind.Sequential)]
    private struct SP_DEVINFO_DATA
    
        public int cbSize;
        public Guid ClassGuid;
        public uint DevInst;
        public IntPtr Reserved;
    

    [Flags]
    private enum DIGCF : uint
    
        DIGCF_DEFAULT = 0x00000001,
        DIGCF_PRESENT = 0x00000002,
        DIGCF_ALLCLASSES = 0x00000004,
        DIGCF_PROFILE = 0x00000008,
        DIGCF_DEVICEINTERFACE = 0x00000010,
    

    [DllImport("setupapi.dll", 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 IntPtr SetupDiGetClassDevs(IntPtr ClassGuid, string Enumerator, IntPtr hwndParent, DIGCF Flags);

    [DllImport("setupapi.dll")]
    private static extern int CM_Get_Parent(out uint pdnDevInst, uint dnDevInst, uint ulFlags);

    [DllImport("setupapi.dll")]
    private static extern int CM_Get_Device_ID(uint dnDevInst, IntPtr Buffer, int BufferLen, uint ulFlags);

    [DllImport("setupapi.dll")]
    private static extern int CM_Get_Child(out uint pdnDevInst, uint dnDevInst, uint ulFlags);

    [DllImport("setupapi.dll")]
    private static extern int CM_Get_Sibling(out uint pdnDevInst, uint dnDevInst, uint ulFlags);

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

    // vista and higher
    [DllImport("setupapi.dll", SetLastError = true, EntryPoint = "SetupDiGetDevicePropertyW")]
    private static extern bool SetupDiGetDeviceProperty(IntPtr DeviceInfoSet, ref SP_DEVINFO_DATA DeviceInfoData, ref DEVPROPKEY propertyKey, out int propertyType, IntPtr propertyBuffer, int propertyBufferSize, out int requiredSize, int flags);

    [StructLayout(LayoutKind.Sequential)]
    private struct DEVPROPKEY
    
        public Guid fmtid;
        public uint pid;

        // from devpkey.h
        public static readonly DEVPROPKEY DEVPKEY_Device_Parent = new DEVPROPKEY  fmtid = new Guid("4340A6C5-93FA-4706-972C-7B648008A5A7"), pid = 8 ;
        public static readonly DEVPROPKEY DEVPKEY_Device_Children = new DEVPROPKEY  fmtid = new Guid("4340A6C5-93FA-4706-972C-7B648008A5A7"), pid = 9 ;
    

    private string[] GetStringListProperty(DEVPROPKEY key)
    
        int type;
        int size;
        SetupDiGetDeviceProperty(_hDevInfo, ref _data, ref key, out type, IntPtr.Zero, 0, out size, 0);
        if (size == 0)
            return new string[0];

        IntPtr buffer = Marshal.AllocHGlobal(size);
        try
        
            if (!SetupDiGetDeviceProperty(_hDevInfo, ref _data, ref key, out type, buffer, size, out size, 0))
                throw new Win32Exception(Marshal.GetLastWin32Error());

            List<string> strings = new List<string>();
            IntPtr current = buffer;
            do
            
                string s = Marshal.PtrToStringUni(current);
                if (string.IsNullOrEmpty(s))
                    break;

                strings.Add(s);
                current += (1 + s.Length) * 2;
            
            while (true);
            return strings.ToArray();
        
        finally
        
            Marshal.FreeHGlobal(buffer);
        
    

    private string GetStringProperty(DEVPROPKEY key)
    
        int type;
        int size;
        SetupDiGetDeviceProperty(_hDevInfo, ref _data, ref key, out type, IntPtr.Zero, 0, out size, 0);
        if (size == 0)
            return null;

        IntPtr buffer = Marshal.AllocHGlobal(size);
        try
        
            if (!SetupDiGetDeviceProperty(_hDevInfo, ref _data, ref key, out type, buffer, size, out size, 0))
                throw new Win32Exception(Marshal.GetLastWin32Error());

            return Marshal.PtrToStringUni(buffer);
        
        finally
        
            Marshal.FreeHGlobal(buffer);
        
    

【讨论】:

我还没有尝试过,但 Vista 或更高版本有点问题。这适用于将在工业环境中使用的 BMS,其中许多环境运行 XP。不过我会看看。非常感谢;这看起来很彻底:) 我已更新设备类代码以支持 Windows XP,但未经过测试。 太棒了!我应该有时间在接下来的几天内开始测试这个。非常感谢! 所以我有机会开始使用它,但对于下一步该做什么我还是有点迷茫(我还是个初学者)。如何使用GetDiskNames 功能?我可以调用它,但我不知道哪种类型的变量应该接收return(或者这甚至是我应该做的)。 foreach(string name in usbDeviceInfo.GetDiskNames()) Console.WriteLine(name) 为例。【参考方案2】:

我遇到了同样的问题,而且浏览 WMI 并没有帮助我,真的。

但我最终得到了这几行代码,它对我很有用:

private string GetAvailableStorageDrive()

    foreach (var info in System.IO.DriveInfo.GetDrives())
    
        if (info.DriveType == System.IO.DriveType.Removable && 
             info.IsReady && 
            !info.Name.Equals("A:\\"))
        
            return info.Name;
        
    
    return string.Empty;

基本上,上面的函数查看DriveType 是否为Removable 以及驱动器是否准备好。 我还排除了驱动器号“A”,因为在默认的 Windows 环境中,这是软盘。

DriveType.Removable 说明:

驱动器是可移动存储设备,例如 软盘驱动器或 USB 闪存驱动器。

注意:正如 CodeCaster 所指出的,此函数还将返回可移动存储设备,例如 SATA。因此,如果是这种情况,您将不得不研究其他人提供的更复杂的解决方案。

【讨论】:

我知道某些读卡器和内部 SATA 磁盘(或其驱动程序)也将自己报告为可移动磁盘。这些通常不是 OP 似乎正在寻找的可移动 USB 大容量存储设备类型。 @CodeCaster 好点。在我的环境中,这非常有意义,因为我收到了 storagedevice-letter。如果 OP 对此感到满意,它比其他方法(如 Simon Mourier 的答案)要容易得多。 @CodeCaster 这也取决于 OP 打算做什么,以及他自己的环境中是否有 SATA 磁盘。也许他很幸运。 :) 抱歉,这不是我真正想要的。我是一名定制电子开发人员,我需要在可能的众多设备中检测我的特定设备。我需要的不仅仅是驱动器号;我需要找出与特定 VID/PID 相关的驱动器号。 @tmwoods 啊,真可惜。我已经希望这对你有用。那么 Simon Mourier 的答案将是您正在寻找的。​​span> 【参考方案3】:

有一个用于设备枚举的古老 Win32 API,它曾经是安装程序 API 的一部分,但已被废弃 - 我只知道它的存在,不知道它的用法,但希望它会有所帮助:

http://msdn.microsoft.com/en-us/library/windows/hardware/ff551015(v=vs.85).aspx

SP_DEVICE_INTERFACE_DETAIL 结构是您的最终目标:它具有实际的设备路径。然而,到达那里的路径......这都是 PInvoke,从签名看起来相当讨厌,但这里有一些参考:

PInvoke 签名:

http://pinvoke.net/default.aspx/setupapi/SetupDiGetClassDevs.html http://pinvoke.net/default.aspx/setupapi/SetupDiEnumDeviceInterfaces.html http://pinvoke.net/default.aspx/setupapi/SetupDiGetDeviceInterfaceDetail.html

结构映射:

http://pinvoke.net/default.aspx/Structures/SP_DEVICE_INTERFACE_DATA.html http://pinvoke.net/default.aspx/Structures/SP_DEVICE_INTERFACE_DETAIL_DATA.html

哦,我找到了一些示例用法(在 C++ 中,但相当可翻译):

http://oroboro.com/usb-serial-number/

【讨论】:

以上是关于从 VID/PID 中查找 USB 驱动器号(XP 和更高版本需要)的主要内容,如果未能解决你的问题,请参考以下文章

解决USB key在citrix虚拟桌面里使用的问题

Delphi 调试连接 任意Android手机/平板/盒子(要安装Google USB Driver,并且还有USB的相关许多文章)

openwrt安装windows驱动

Visual Studio Express 可以从 USB 驱动器运行吗?

从USB驱动器运行Windows 10

Windows PC 作为 USB 从设备来模拟拇指驱动器