简体   繁体   English

从VID / PID查找USB驱动器盘符(需要XP及更高版本)

[英]Find USB drive letter from VID/PID (Needed for XP and higher)

So I thought I would include the final answer here so you don't have to make sense of this post. 所以我想我会在这里包括最终答案,所以你不必理解这篇文章。 Big thanks to Simon Mourier for taking the time to figure this one out. 非常感谢Simon Mourier抽出时间来解决这个问题。

MY WORKING CODE 我的工作代码

        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();
                    }
                }                   
            }

So just use GetUSBDevices from my original question and then include the two classes shown by Simon Mourier's answer and it should be good to go! 所以只需使用原始问题中的GetUSBDevices ,然后包含Simon Mourier回答所显示的两个类,它应该不错!


ORIGINAL QUESTION 原始问题

I know this question has been asked before (see here ) but none of them have a confirmed answer and I've tried all of the suggested answers. 我知道之前已经问过这个问题(见这里 )但是他们都没有得到确认答案,我已经尝试了所有建议的答案。 Unfortunately those threads are long dead and I was hoping someone could give a better answer here. 不幸的是,这些线程已经很久了,我希望有人能在这里给出更好的答案。

So far I have two 'starting points', each of which I'll show below. 到目前为止,我有两个“起点”,我将在下面展示。


OPTION 1 : (gets VID/PID but not drive letter) 选项1 :(获取VID / PID但不是驱动器号)

I have an embedded device which I connect to through an application. 我有一个嵌入式设备,我通过应用程序连接到该设备。 I have code which succesfully scans any USB devices and checks the VID/PID . 我有代码可以成功扫描任何USB设备并检查VID/PID I successfully detect my device but I have no idea how to get the drive letter. 我成功检测到我的设备,但我不知道如何获取驱动器号。 Can someone help me out? 有人可以帮我吗? I feel like I could add another line in the class but when I go through Device Manager I can't find any property there that describes the drive letter. 我觉得我可以在class添加另一行,但是当我通过Device Manager我找不到任何描述驱动器号的属性。

Thanks! 谢谢!

I'll include my code so far below. 到目前为止,我将包含我的代码。

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
            }
        }
    }

where those functions are: 这些功能是:

 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;
    }      

and the class is: 而且课程是:

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; }
}

OPTION 2 : (get drive letter but not VID/PID) 选项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();

                    }
                }
            }

I'm going to guess the VID/PID is in one of the disk object properties but I just can't find which one. 我要猜测VID / PID是在其中一个disk对象属性中,但我找不到哪一个。

I may be mistaken but it seems WMI doesn't know about the parent-child relation that exists in the Windows device setup API. 我可能会弄错,但似乎WMI不知道Windows设备安装API中存在的父子关系。

So, I have created a small Device utility class that can add this missing link from the native Setup API. 因此,我创建了一个小型Device实用程序类,可以从本机Setup API添加此缺少的链接。 Here is how you would use it in your original USBDeviceInfo class: 以下是在原始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"];
                        }
                    }
                }
            }
        }
    }
}

And here is the new Device class: 这是新的Device类:

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);
        }
    }
}

I've had the same problem and also browsing through the WMI did not help me out, really. 我有同样的问题,浏览WMI并没有帮助我,真的。

But I ended up with these few lines of code, and it works great for me: 但我最终得到了这几行代码,它对我很有用:

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;
}

Basically the above function looks if the DriveType is Removable andalso if the Drive is ready. 基本上,上面的函数查看DriveType是否Removable以及Drive是否准备就绪。 I also exclude the drive letter 'A' because in default windows environments, this is the Floppy. 我还排除了驱动器号'A',因为在默认的Windows环境中,这是软盘。

Description of DriveType.Removable: DriveType.Removable的描述:

The drive is a removable storage device, such as a floppy disk drive or a USB flash drive. 该驱动器是可移动存储设备,例如软盘驱动器或USB闪存驱动器。

Note: As CodeCaster pointed out, this function will also return removable storage devices such as SATA. 注意:正如CodeCaster所指出的,此功能还将返回可移动存储设备,如SATA。 So if that's the case, you will have to look into more complex solutions as provided by others. 因此,如果是这种情况,您将不得不研究其他人提供的更复杂的解决方案。

There's an ancient Win32 API for device enumeration, it used to be part of the installer API but has fallen into disuse - I'm only aware of its existence, notsomuch its usage, but hopefully it'll be helpful: 有一个古老的Win32 API用于设备枚举,它曾经是安装程序API的一部分,但已经废弃 - 我只知道它的存在,不是它的用法,但希望它会有所帮助:

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

The SP_DEVICE_INTERFACE_DETAIL structure is your end-goal: it has the actual device path. SP_DEVICE_INTERFACE_DETAIL结构是您的最终目标:它具有实际的设备路径。 The path to get there, however... It'd all be PInvoke, and fairly nasty looking from the signatures, but here are some references: 然而,到达那里的路径......它都是PInvoke,从签名看起来相当讨厌,但这里有一些参考:

The PInvoke signatures: 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/setupapi/SetupDiGetClassDevs.html http://pinvoke.net/default.aspx/setupapi/SetupDiEnumDeviceInterfaces.html http://pinvoke.net/default.aspx/setupapi/SetupDiGetDeviceInterfaceDetail。 HTML

The structure mappings: 结构映射:

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

Ooh, I found some example usage (in C++, but fairly translatable): 哦,我发现了一些示例用法(在C ++中,但可以翻译):

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM