Определите COM-порт, используя VID и PID для USB-устройства, подключенного к x64.

Как показано ниже, я могу получить имена портов usb com, подключенные к 32-битной машине с ОС win7OS, по заданным pid и vid, но при работе в 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;
    }

фактический код получить здесь Итак, как получить имена COM-портов в x64, есть предложения?


person UdayaLakmal    schedule 27.04.2012    source источник
comment
Это невозможно угадать, по крайней мере требуется трассировка стека.   -  person Hans Passant    schedule 27.04.2012
comment
этот метод возвращает список COM-портов, к которым подключено наше устройство, например: COM3, COM4 и т. д. Проблема в том, что это не работает на x64.   -  person UdayaLakmal    schedule 30.04.2012
comment
Поскольку это может быть актуально для других, которые приходят сюда: простая причина, вероятно, связана с тем, что rk6 является null, поскольку ключ "Device Parameters" существует не на каждом узле устройства и, как таковой, не связан с 32-битной или 64-битной версией ОС.   -  person NiKiZe    schedule 07.09.2016
comment
@UdayaLakmal, ответ Юкко решил вашу проблему? Если да, то можете ли вы принять это?   -  person cp.engr    schedule 20.12.2016


Ответы (5)


Прочитав ваш код, я обнаружил, что текущий путь, который вы просматриваете в реестре, не содержит никакой информации о портах. Но я нашел способ прочитать это, сделав небольшое изменение:

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

Это сработало отлично. Кстати, спасибо за ваш код... Он мне очень помог!

person Youkko    schedule 27.08.2012
comment
Кроме того, используйте if (!String.IsNullOrEmpty(location)) вместо if (location != string.Empty)... Я обнаружил, что location может быть нулевым. - person dlchambers; 13.06.2013
comment
LocationInformation — аппаратное расположение USB-устройства, не номер COM-порта - person NiKiZe; 07.09.2016

Пока я тестировал ответ от Youkko под Windows 10 x64, я получил некоторые странные результаты и просматривал реестр на своей машине. ключи LocationInformation содержали такие строки, как Port_#0002.Hub_#0003, поэтому они связаны с USB-концентратором / портом, к которому подключено устройство, а не с COM-портом, выделенным Windows.

Итак, в моем случае я включил 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;
}
person PeterJ    schedule 21.01.2016
comment
В то время, когда я ответил, даже Windows 8 не существовало. Мой ответ работает под Windows 7. Но большое спасибо за этот вклад. Каждый ответ иногда нуждается в обновлениях :-) - person Youkko; 13.05.2016

Вот мой взгляд на это (даже если это не прямая А к Q)

  • USB-устройства всегда (и всегда) перечисляются под HKLM\SYSTEM\CurrentControlSet\Enum\USB (примечание USB в конце)
    • Device nodes have the format VID_xxxx&PID_xxxx* where xxxx is hexadecimal, there might be some extra function data at the end
    • Each device node has a sub identifier node based on serialnumber or other data of the device and functions
    • identifier node can have value "FriendlyName" which some times have the COM in parantheses such as "Virtual Serial Port (COM6)"
    • Resulting path: HKLM\SYSTEM\CurrentControlSet\Enum\USB\VID_xxxx&PID_xxxx*\*\Device Parameters\ has value named "PortName"
  • Доступные в настоящее время COM-порты перечислены System.IO.Ports.SerialPort.GetPortNames()
  • 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);
        }
    }
    
person NiKiZe    schedule 07.09.2016

Я думаю, ManagementObjectSearcher может быть лучшим подходом, чем непосредственное чтение реестра.

Вот пример для виртуальных COM-портов.

person PhilMY    schedule 27.04.2012
comment
Чтобы добавить к этому, попробуйте загрузить WMI Code Creator microsoft.com /en-us/download/confirmation.aspx?id=8572 . Установите пространство имен в root\WMI и классы в MSSerial_PortName, после чего вы сможете изменить средство поиска ManagementObjectSearcher, чтобы запросить, что «InstanceName» (информация USB для com-порта) содержит определенную строку (например, PID_7523). Если я получу рабочий пример, я поставлю его здесь как ответ позже. - person drojf; 29.11.2017
comment
Похоже, что с этим методом возникают различные проблемы (например, Windows не всегда указывает номер com-порта в описании, ошибки доступа запрещены при использовании root\WMI / сразу com-порты не отображаются на win10). Метод PeterJ кажется лучшим. - person drojf; 30.11.2017

Хорошо, используя 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);
        }
person ben-ben    schedule 21.11.2016