问题
I am trying to find the correct way of finding the COM port name if I know the PID and VID. So far I wrote the workaround but i do not believe that there is no more elegant and correct way. BTW (I know that I can use REGEX). This was written only to test the workaround.
I know that there is a lots of space for improvements and I am not asking for a code review
Any ideas are more than welcome :)
namespace ConsoleApplication1
{
using System;
using System.Globalization;
using System.Collections.Generic;
using System.Management;
using System.Reflection;
class Program
{
static void Main(string[] args)
{
VCOMDetect VCOMPorts = new VCOMDetect(0x0483, 0xA27D);
Console.WriteLine("BootPort: {0}", VCOMPorts.BootPort() ?? "Not detected");
Console.WriteLine("Oscilloscope Port: {0}", VCOMPorts.OscilloscopePort() ?? "Not detected");
Console.WriteLine("Serial/USB Port: {0}", VCOMPorts.SerialPort() ?? "Not detected");
Console.WriteLine("Programming Port: {0}", VCOMPorts.ProgPort() ?? "Not detected");
Console.Read();
}
}
class VCOMDetect
{
private const string USB_BOOTLOADER_SERIAL = "000000000b00";
private const string USB_OSCILLOSCOPE_SERIAL = "000000000c00";
private const string USB_VCOM_SERIAL = "000000000d00";
private const string USB_PRG_SERIAL = "000000000e00";
private List<VCOM_USBDeviceInfo> PortsList;
public VCOMDetect(UInt16 vid, UInt16 pid)
{
PortsList = GetUSBDevices(vid, pid);
}
public string BootPort()
{
foreach(VCOM_USBDeviceInfo VCOM in PortsList)
{
if (VCOM.Serial.ToLower() == USB_BOOTLOADER_SERIAL.ToLower()) return VCOM.COMPort;
}
return null;
}
public string OscilloscopePort()
{
foreach (VCOM_USBDeviceInfo VCOM in PortsList)
{
if (VCOM.Serial.ToLower() == USB_OSCILLOSCOPE_SERIAL.ToLower()) return VCOM.COMPort;
}
return null;
}
public string SerialPort()
{
foreach (VCOM_USBDeviceInfo VCOM in PortsList)
{
if (VCOM.Serial.ToLower() == USB_VCOM_SERIAL.ToLower()) return VCOM.COMPort;
}
return null;
}
public string ProgPort()
{
foreach (VCOM_USBDeviceInfo VCOM in PortsList)
{
if (VCOM.Serial.ToLower() == USB_PRG_SERIAL.ToLower()) return VCOM.COMPort;
}
return null;
}
private List<VCOM_USBDeviceInfo> GetUSBDevices(UInt16 vid, UInt16 pid)
{
List<VCOM_USBDeviceInfo> VCOM_devices = new List<VCOM_USBDeviceInfo>();
ManagementObjectCollection collection;
using (var searcher = new ManagementObjectSearcher(@"Select * From Win32_PnPEntity"))
collection = searcher.Get();
foreach (var device in collection)
{
var USBInfo = new VCOM_USBDeviceInfo((string)device.GetPropertyValue("DeviceID"));
if (USBInfo.PID == 0 || USBInfo.VID == 0) continue;
if (USBInfo.PID != pid || USBInfo.VID != vid) continue;
USBInfo.Caption = (string)device.GetPropertyValue("Caption");
if (USBInfo.COMPort == "") continue;
USBInfo.PnpDeviceID = (string)device.GetPropertyValue("PNPDeviceID");
USBInfo.Description = (string)device.GetPropertyValue("Description");
VCOM_devices.Add(USBInfo);
}
collection.Dispose();
return VCOM_devices;
}
}
class VCOM_USBDeviceInfo
{
private UInt16 _PID, _VID;
private string _Caption;
private void _ResetData()
{
this.PID = 0;
this.VID = 0;
this.COMnumber = -1;
this.COMPort = "";
this.Serial = "";
}
public VCOM_USBDeviceInfo(string DeviceID)
{
int VIDIndex = DeviceID.IndexOf("VID_");
int PIDIndex = DeviceID.IndexOf("PID_");
int VIDIndexEnd = -1;
int PIDIndexEnd = -1;
string PIDSubstring, VIDSubstring;
if (PIDIndex == -1 || VIDIndex == -1)
{
_ResetData();
}
else
{
bool result = true;
PIDSubstring = DeviceID.Substring(PIDIndex + 4);
VIDSubstring = DeviceID.Substring(VIDIndex + 4);
PIDIndexEnd = PIDSubstring.IndexOf("\\");
VIDIndexEnd = VIDSubstring.IndexOf("&");
if(PIDIndexEnd == -1 || VIDIndexEnd == -1)
{
_ResetData();
}
else
{
result = result && UInt16.TryParse(PIDSubstring.Substring(0, PIDIndexEnd), NumberStyles.AllowHexSpecifier, null, out _PID) && UInt16.TryParse(VIDSubstring.Substring(0, VIDIndexEnd), NumberStyles.AllowHexSpecifier, null, out _VID);
if(!result)
{
_ResetData();
}
else
{
PID = _PID;
VID = _VID;
Serial = PIDSubstring.Substring(PIDIndexEnd + 1);
}
}
}
}
public string DeviceID { get; set; }
public string PnpDeviceID { get; set; }
public string Description { get; set; }
public string Caption
{
get
{
return _Caption;
}
set
{
int COMindex = value.IndexOf("(COM");
string tmpCOMPort = COMindex == -1 ? "" : value.Substring(COMindex + 1, 4);
if(COMPort == null || COMPort.Length == 0)
{
COMPort = tmpCOMPort;
}
else
{
if(COMPort != tmpCOMPort)
{
Console.WriteLine("Inconsistent COM port information");
}
}
_Caption = value;
}
}
public UInt16 PID { get; set; }
public UInt16 VID { get; set; }
public string Serial { get; set; }
public int COMnumber { get; set; }
public string COMPort { get; set; }
}
}
It does the job - but I di not like it
回答1:
I actually think your code looks good. Yes, I'd use the Regex and there are places, where things can be condensed. But, that stuff won't help improve performance or do anything else. Just, mostly, make the code smaller. I'm also assuming this app will run on Windows only.
Anyhow, can any of this code help:
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Text.RegularExpressions;
namespace PortNames
{
class Program
{
static List<string> ComPortNames(String VID, String PID)
{
RegistryKey rk1 = Registry.LocalMachine;
RegistryKey rk2 = rk1.OpenSubKey("SYSTEM\\CurrentControlSet\\Enum");
String pattern = String.Format("^VID_{0}.PID_{1}", VID, PID);
Regex _rx = new Regex(pattern, RegexOptions.IgnoreCase);
List<string> ports = new List<string>();
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");
ports.Add((string)rk6.GetValue("PortName"));
}
}
}
}
return ports;
}
static void Main(string[] args)
{
List<string> names = ComPortNames("0403", "6001");
if (names.Count > 0) foreach (String s in SerialPort.GetPortNames()) { Console.WriteLine(s); }
Console.ReadLine();
}
}
}
or
using System;
using System.Collections.Generic;
using System.Linq;
using System.Management;
using System.Text.RegularExpressions;
namespace PortNames
{
class Program
{
private const string vidPattern = @"VID_([0-9A-F]{4})";
private const string pidPattern = @"PID_([0-9A-F]{4})";
struct ComPort // custom struct with our desired values
{
public string name;
public string vid;
public string pid;
public string description;
}
private static List<ComPort> GetSerialPorts()
{
using (var searcher = new ManagementObjectSearcher
("SELECT * FROM WIN32_SerialPort"))
{
var ports = searcher.Get().Cast<ManagementBaseObject>().ToList();
return ports.Select(p =>
{
ComPort c = new ComPort();
c.name = p.GetPropertyValue("DeviceID").ToString();
c.vid = p.GetPropertyValue("PNPDeviceID").ToString();
c.description = p.GetPropertyValue("Caption").ToString();
Match mVID = Regex.Match(c.vid, vidPattern, RegexOptions.IgnoreCase);
Match mPID = Regex.Match(c.vid, pidPattern, RegexOptions.IgnoreCase);
if (mVID.Success)
c.vid = mVID.Groups[1].Value;
if (mPID.Success)
c.pid = mPID.Groups[1].Value;
return c;
}).ToList();
}
}
static void Main(string[] args)
{
List<ComPort> ports = GetSerialPorts();
//if we want to find one device
ComPort com = ports.FindLast(c => c.vid.Equals("0483") && c.pid.Equals("5740"));
//or if we want to extract all devices with specified values:
List<ComPort> coms = ports.FindAll(c => c.vid.Equals("0483") && c.pid.Equals("5740"));
Console.ReadLine();
}
}
}
来源:https://stackoverflow.com/questions/50543886/how-to-discover-the-virtual-com-port-name-knowing-the-pid-and-vid-in-c-sharp