【问题标题】:Identify COM port using VID and PID for USB device attached to x64使用 VID 和 PID 识别连接到 x64 的 USB 设备的 COM 端口
【发布时间】:2016-11-21 20:55:37
【问题描述】:

如下所示,我能够通过给定的 pid 和 vid 将 USB com 端口名称附加到 32 位 win7OS 机器,但是在 x64 中运行时,它卡在以下行:

comports.Add((string)rk6.GetValue("PortName"));

这是我的代码

static List<string> ComPortNames(String VID, String PID)
    {
        String pattern = String.Format("^VID_{0}.PID_{1}", VID, PID);
        Regex _rx = new Regex(pattern, RegexOptions.IgnoreCase);
        List<string> comports = new List<string>();

        RegistryKey rk1 = Registry.LocalMachine;
        RegistryKey rk2 = rk1.OpenSubKey("SYSTEM\\CurrentControlSet\\Enum");

        foreach (String s3 in rk2.GetSubKeyNames())
        {

            RegistryKey rk3 = rk2.OpenSubKey(s3);
            foreach (String s in rk3.GetSubKeyNames())
            {
                if (_rx.Match(s).Success)
                {
                    RegistryKey rk4 = rk3.OpenSubKey(s);
                    foreach (String s2 in rk4.GetSubKeyNames())
                    {
                        RegistryKey rk5 = rk4.OpenSubKey(s2);
                        RegistryKey rk6 = rk5.OpenSubKey("Device Parameters");
                        comports.Add((string)rk6.GetValue("PortName"));
                    }
                }
            }
        }
        return comports;
    }

实际代码获取here,那么如何获取x64中的com端口名称,有什么建议吗?

【问题讨论】:

  • 这无法猜测,至少需要堆栈跟踪。
  • 此方法返回连接我们设备的 com 端口列表,例如:COM3、COM4 等,问题是这在 x64 上不起作用
  • 因为它可能与这里的其他人相关:简单的原因可能是由于rk6null,因为"Device Parameters" 密钥并不存在于每个设备节点上,因此是与 32 位或 64 位版本的操作系统无关
  • @UdayaLakmal,Youkko 的回答是否解决了您的问题?如果可以,你能接受吗?

标签: c# usb 64-bit usbserial


【解决方案1】:

当我在 Windows 10 x64 下测试来自 Youkko 的答案时,我得到了一些奇怪的结果,并查看了我机器上的注册表,LocationInformation 键包含诸如 Port_#0002.Hub_#0003 这样的字符串,因此它们与 USB 相关hub/port 设备连接到的不是 Windows 分配的 COM 端口。

所以在我的情况下,我得到了 COM2,它是我主板上的一个硬件端口,它跳过了我期待的 COM5 端口,但它位于 PortName 注册表项下。我不确定自您使用的 Windows 版本以来是否发生了某些变化,但我认为您的主要问题可能是没有检查键上的空值。

以下稍作修改的版本似乎可以在各种 Windows 7 / 10 和 x32 / 64 系统上正常工作,我还添加了一个检查 SerialPort.GetPortNames() 以确保设备可用并插入系统在返回之前:

static List<string> ComPortNames(String VID, String PID)
{
    String pattern = String.Format("^VID_{0}.PID_{1}", VID, PID);
    Regex _rx = new Regex(pattern, RegexOptions.IgnoreCase);
    List<string> comports = new List<string>();

    RegistryKey rk1 = Registry.LocalMachine;
    RegistryKey rk2 = rk1.OpenSubKey("SYSTEM\\CurrentControlSet\\Enum");

    foreach (String s3 in rk2.GetSubKeyNames())
    {
        RegistryKey rk3 = rk2.OpenSubKey(s3);
        foreach (String s in rk3.GetSubKeyNames())
        {
            if (_rx.Match(s).Success)
            {
                RegistryKey rk4 = rk3.OpenSubKey(s);
                foreach (String s2 in rk4.GetSubKeyNames())
                {
                    RegistryKey rk5 = rk4.OpenSubKey(s2);
                    string location = (string)rk5.GetValue("LocationInformation");
                    RegistryKey rk6 = rk5.OpenSubKey("Device Parameters");
                    string portName = (string)rk6.GetValue("PortName");
                    if (!String.IsNullOrEmpty(portName) && SerialPort.GetPortNames().Contains(portName))
                        comports.Add((string)rk6.GetValue("PortName"));
                }
            }
        }
    }
    return comports;
}

【讨论】:

  • 在我回答的时候,连 Windows 8 都不存在。我的答案适用于 Windows 7。但非常感谢您的贡献。每个答案有时都需要更新:-)
【解决方案2】:

通过阅读您的代码,我发现您在注册表中查看的当前路径不包含任何有关端口的信息。 但是我通过做这个小改动找到了一种阅读方式:

    static List<string> ComPortNames(String VID, String PID)
    {
        String pattern = String.Format("^VID_{0}.PID_{1}", VID, PID);
        Regex _rx = new Regex(pattern, RegexOptions.IgnoreCase);
        List<string> comports = new List<string>();

        RegistryKey rk1 = Registry.LocalMachine;
        RegistryKey rk2 = rk1.OpenSubKey("SYSTEM\\CurrentControlSet\\Enum");

        foreach (String s3 in rk2.GetSubKeyNames())
        {

            RegistryKey rk3 = rk2.OpenSubKey(s3);
            foreach (String s in rk3.GetSubKeyNames())
            {
                if (_rx.Match(s).Success)
                {
                    RegistryKey rk4 = rk3.OpenSubKey(s);
                    foreach (String s2 in rk4.GetSubKeyNames())
                    {
                        RegistryKey rk5 = rk4.OpenSubKey(s2);
                        string location = (string)rk5.GetValue("LocationInformation");
                        if (!String.IsNullOrEmpty(location))
                        {
                            string port = location.Substring(location.IndexOf('#') + 1, 4).TrimStart('0');
                            if (!String.IsNullOrEmpty(port)) comports.Add(String.Format("COM{0:####}", port));
                        }
                        //RegistryKey rk6 = rk5.OpenSubKey("Device Parameters");
                        //comports.Add((string)rk6.GetValue("PortName"));
                    }
                }
            }
        }
        return comports;
    }

它确实工作得很好。 顺便说一句,谢谢你的代码……它对我帮助很大!

【讨论】:

  • 另外,使用“if (!String.IsNullOrEmpty(location))”而不是“if (location != string.Empty)”...我发现 location 可以为 null。
  • LocationInformation 是 USB 设备的硬件位置,不是 COM 端口号
【解决方案3】:

这是我对此的看法(即使它不是直接的 A 到 Q)

  • USB 设备总是(并且一直)在HKLM\SYSTEM\CurrentControlSet\Enum\USB 下枚举(最后注意USB
    • 设备节点的格式为VID_xxxx&amp;PID_xxxx*,其中xxxx是十六进制,末尾可能有一些额外的函数数据
    • 每个设备节点都有一个基于序列号或设备和功能的其他数据的子标识符节点
    • 标识符节点可以有值"FriendlyName",有时在括号中包含 COM,例如“虚拟串行端口 (COM6)”
    • 结果路径:HKLM\SYSTEM\CurrentControlSet\Enum\USB\VID_xxxx&amp;PID_xxxx*\*\Device Parameters\ 具有名为 "PortName" 的值
  • System.IO.Ports.SerialPort.GetPortNames() 列出了当前可用的 com 端口
  • OpenSubKey 实现 IDisposable 并且应该有 using.Dispose()

    using System.IO.Ports;
    using System.Linq;
    using Microsoft.Win32;
    
    public class UsbSerialPort
    {
        public readonly string PortName;
        public readonly string DeviceId;
        public readonly string FriendlyName;
    
        private UsbSerialPort(string name, string id, string friendly)
        {
            PortName = name;
            DeviceId = id;
            FriendlyName = friendly;
        }
    
        private static IEnumerable<RegistryKey> GetSubKeys(RegistryKey key)
        {
            foreach (string keyName in key.GetSubKeyNames())
                using (var subKey = key.OpenSubKey(keyName))
                    yield return subKey;
        }
    
        private static string GetName(RegistryKey key)
        {
            string name = key.Name;
            int idx;
            return (idx = name.LastIndexOf('\\')) == -1 ?
                name : name.Substring(idx + 1);
        }
    
        public static IEnumerable<UsbSerialPort> GetPorts()
        {
            var existingPorts = SerialPort.GetPortNames();
            using (var enumUsbKey = Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Enum\USB"))
            {
                if (enumUsbKey == null)
                    throw new ArgumentNullException("USB", "No enumerable USB devices found in registry");
                foreach (var devBaseKey in GetSubKeys(enumUsbKey))
                {
                    foreach (var devFnKey in GetSubKeys(devBaseKey))
                    {
                        string friendlyName =
                            (string) devFnKey.GetValue("FriendlyName") ??
                            (string) devFnKey.GetValue("DeviceDesc");
                        using (var devParamsKey = devFnKey.OpenSubKey("Device Parameters"))
                        {
                            string portName = (string) devParamsKey?.GetValue("PortName");
                            if (!string.IsNullOrEmpty(portName) &&
                                existingPorts.Contains(portName))
                                yield return new UsbSerialPort(portName, GetName(devBaseKey) + @"\" + GetName(devFnKey), friendlyName);
                        }
                    }
    
                }
            }
        }
    
        public override string ToString()
        {
            return string.Format("{0} Friendly: {1} DeviceId: {2}", PortName, FriendlyName, DeviceId);
        }
    }
    

【讨论】:

    【解决方案4】:

    我认为ManagementObjectSearcher 可能是比直接读取注册表更好的方法。

    这里是an example,用于虚拟 COM 端口。

    【讨论】:

    • 要添加此内容,请尝试下载 WMI Code Creator microsoft.com/en-us/download/confirmation.aspx?id=8572。将命名空间设置为“root\WMI”,将类设置为“MSSerial_PortName”,然后您应该能够修改 ManagementObjectSearcher 搜索器以查询“InstanceName”(com 端口的 USB 信息)是否包含特定字符串(例如“ PID_7523") 。如果我得到一个工作示例,我稍后会将其作为答案。
    • 这种方法似乎存在各种问题(例如windows并不总是将com端口号放在描述中,使用root\WMI时访问被拒绝错误/win10上完全没有显示com端口)。 PeterJ 的方法似乎是迄今为止最好的。
    【解决方案5】:

    好的,使用 ManagementObjectSearcher(如果存在,它会给出 COM 端口索引和 VID 和 PID):

            List < List <string>> USBCOMlist = new List<List<string>>();
            try
            {
                ManagementObjectSearcher searcher =
                    new ManagementObjectSearcher("root\\CIMV2",
                    "SELECT * FROM Win32_PnPEntity");
    
                foreach (ManagementObject queryObj in searcher.Get())
                {
                    if (queryObj["Caption"].ToString().Contains("(COM"))
                    {
                        List<string> DevInfo = new List<string>();
    
                        string Caption = queryObj["Caption"].ToString();
                        int CaptionIndex = Caption.IndexOf("(COM");
                        string CaptionInfo = Caption.Substring(CaptionIndex + 1).TrimEnd(')'); // make the trimming more correct                 
    
                        DevInfo.Add(CaptionInfo);
    
                        string deviceId = queryObj["deviceid"].ToString(); //"DeviceID"
    
                        int vidIndex = deviceId.IndexOf("VID_");
                        int pidIndex = deviceId.IndexOf("PID_");
                        string vid = "", pid = "";
    
                        if (vidIndex != -1 && pidIndex != -1)
                        {
                            string startingAtVid = deviceId.Substring(vidIndex + 4); // + 4 to remove "VID_"                    
                            vid = startingAtVid.Substring(0, 4); // vid is four characters long
                                                                 //Console.WriteLine("VID: " + vid);
                            string startingAtPid = deviceId.Substring(pidIndex + 4); // + 4 to remove "PID_"                    
                            pid = startingAtPid.Substring(0, 4); // pid is four characters long
                        }
    
                        DevInfo.Add(vid);
                        DevInfo.Add(pid);
    
                        USBCOMlist.Add(DevInfo);
                    }
    
                }
            }
            catch (ManagementException e)
            {
                MessageBox.Show(e.Message);
            }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-01-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-06
      相关资源
      最近更新 更多