I am very new to embedded programming, I am tryig to get all port names in a list. I guess this is the most basic operation.
using System.IO.Ports;
string[] ports = SerialPort.GetPortNames();
Not sure where I am going wring with this basic operation, but the string is empty. Any leads to what i am doing wrong would be helpful
GetPortNames will only gather valid connected COM ports.
If you are trying to gather the COM port for a disconnected port you will need to search through the registry. Here's how I find the correct device I want based off VID and PID.
public class ComPortFinder
{
public static List<DeviceInfo> FindConnectedDevices(uint vid, uint pid)
{
string pattern = string.Format("^VID_{0:X4}.PID_{1:X4}", vid, pid);
Regex _rx = new Regex(pattern, RegexOptions.IgnoreCase);
List<DeviceInfo> devices = new List<DeviceInfo>();
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");
if (!string.IsNullOrEmpty((string)rk6.GetValue("PortName")))
{
DeviceInfo di = new DeviceInfo()
{
VenderId = vid,
ProductId = pid,
SerialNumber = "UNKNOWN",
ComPort = rk6.GetValue("PortName").ToString()
};
devices.Add(di);
}
}
}
}
}
return devices;
}
}
public struct DeviceInfo
{
public uint VenderId;
public uint ProductId;
public string SerialNumber;
public string ComPort;
}
I do not take credit for this, as I believe this was stolen from another StackOverflow answer but hopefully it will help.
According to Juanma's answer you can get all ports by using wmi instruments in here How to find available COM ports?
try
{
ManagementObjectSearcher searcher =
new ManagementObjectSearcher("root\\WMI",
"SELECT * FROM MSSerial_PortName");
foreach (ManagementObject queryObj in searcher.Get())
{
Console.WriteLine("-----------------------------------");
Console.WriteLine("MSSerial_PortName instance");
Console.WriteLine("-----------------------------------");
Console.WriteLine("InstanceName: {0}", queryObj["InstanceName"]);
Console.WriteLine("-----------------------------------");
Console.WriteLine("MSSerial_PortName instance");
Console.WriteLine("-----------------------------------");
Console.WriteLine("PortName: {0}", queryObj["PortName"]);
//If the serial port's instance name contains USB
//it must be a USB to serial device
if (queryObj["InstanceName"].ToString().Contains("USB"))
{
Console.WriteLine(queryObj["PortName"] + "
is a USB to SERIAL adapter/converter");
}
}
}
catch (ManagementException e)
{
MessageBox.Show("An error occurred while querying for WMI data: " + e.Message);
}
don't forget to add System.Management reference
Related
I have winform application working on USB, I distribute the application on USB for the clients, I'm checking the the USB serial number if the application moved to another USB the application shows a message to the user that he cant run this app because he is not registered, I have a method getting the USB serial number by USB letter, but the problem is windows changing the USB letter dynamically, so its hard to get the USB letter and I cannot make the letter fixed so I can't read it.
I'm looking for a way to get the serial number for the USB by the USB Name is That possible ?? and if not what is the best way to manage my problem ??
Here is the Class I use to get the USB serial number :
class USBSerialNumber
{
string _serialNumber;
string _driveLetter;
public string getSerialNumberFromDriveLetter(string driveLetter)
{
this._driveLetter = driveLetter.ToUpper();
if (!this._driveLetter.Contains(":"))
{
this._driveLetter += ":";
}
matchDriveLetterWithSerial();
return this._serialNumber;
}
private void matchDriveLetterWithSerial()
{
string[] diskArray;
string driveNumber;
string driveLetter;
ManagementObjectSearcher searcher1 = new ManagementObjectSearcher("SELECT * FROM Win32_LogicalDiskToPartition");
foreach (ManagementObject dm in searcher1.Get())
{
diskArray = null;
driveLetter = getValueInQuotes(dm["Dependent"].ToString());
diskArray = getValueInQuotes(dm["Antecedent"].ToString()).Split(',');
driveNumber = diskArray[0].Remove(0, 6).Trim();
if (driveLetter == this._driveLetter)
{
/* This is where we get the drive serial */
ManagementObjectSearcher disks = new ManagementObjectSearcher("SELECT * FROM Win32_DiskDrive");
foreach (ManagementObject disk in disks.Get())
{
if (disk["Name"].ToString() == ("\\\\.\\PHYSICALDRIVE" + driveNumber) & disk["InterfaceType"].ToString() == "USB")
{
this._serialNumber = parseSerialFromDeviceID(disk["PNPDeviceID"].ToString());
}
}
}
}
}
private string parseSerialFromDeviceID(string deviceId)
{
string[] splitDeviceId = deviceId.Split('\\');
string[] serialArray;
string serial;
int arrayLen = splitDeviceId.Length - 1;
serialArray = splitDeviceId[arrayLen].Split('&');
serial = serialArray[0];
return serial;
}
private string getValueInQuotes(string inValue)
{
string parsedValue = "";
int posFoundStart = 0;
int posFoundEnd = 0;
posFoundStart = inValue.IndexOf("\"");
posFoundEnd = inValue.IndexOf("\"", posFoundStart + 1);
parsedValue = inValue.Substring(posFoundStart + 1, (posFoundEnd - posFoundStart) - 1);
return parsedValue;
}
}
May be the title is duplicate. I am getting HDD of the laptop serial number successfully when no USB devices are connected. But when any USB is connected, the code gets the serial number of connected device. I only want the serial number of HDD of laptop or desktop even though USBs are connected.
Below is the code.
using System.Management;
namespace SystemInfo
{
public class Info1
{
public static String GetHDDSerialNo()
{
ManagementClass mangnmt = new ManagementClass("Win32_LogicalDisk");
ManagementObjectCollection mcol = mangnmt.GetInstances();
string result = "";
foreach (ManagementObject strt in mcol)
{
result += Convert.ToString(strt["VolumeSerialNumber"]);
}
return result;
}
}
}
try this
ManagementObjectSearcher theSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_DiskDrive WHERE InterfaceType='USB'");
foreach (ManagementObject currentObject in theSearcher.Get())
{
ManagementObject theSerialNumberObjectQuery = new ManagementObject("Win32_PhysicalMedia.Tag='" + currentObject["DeviceID"] + "'");
MessageBox.Show(theSerialNumberObjectQuery["SerialNumber"].ToString());
}
You can use WMI Win32_DiskDrive, filter on MediaType containing "fixed" and get the SerialNumber
Something like :
public static String GetHDDSerialNo()
{
ManagementClass mangnmt = new ManagementClass("Win32_DiskDrive");
ManagementObjectCollection mcol = mangnmt.GetInstances();
string result = "";
foreach (ManagementObject strt in mcol)
{
if (Convert.ToString(strt["MediaType"]).ToUpper().Contains("FIXED"))
{
result += Convert.ToString(strt["SerialNumber"]);
}
}
return result;
}
Media type can contain "External", "Removable", "fixed". Exact string depends on OS. On Seven and XP, that String can be different. That's why we use Contains.
little reading
This question has been asked before, and there is one answer that supposedly works here. But I've tried it out and it does not work for me.
The issue is that the PNPDeviceID returned by the Query on Win32_DiskDrive and that returned by the "Device" class are different. For example in my case the Query returns something like - PNPDeviceID: USBSTOR\DISK&VEN_ABCD&PROD_1234&REV_0001\8&2C3C9390&0 while the Device class returns the actual VID/PID combination --> USB\VID_4568&PID_QWER&MI_00\7&15b8d7f0&3&0000.
So the SELECT query on Win32_DiskDrive always fails.
Main Code:
var usbDevices = GetUSBDevices();
//Enumerate the USB devices to see if any have specific VID/PID
foreach (var usbDevice in usbDevices)
{
if (usbDevice.DeviceID.Contains("ABCD") && usbDevice.DeviceID.Contains("1234"))
{
foreach (string name in usbDevice.GetDiskNames())
{
//Open dialog to show file names
Debug.WriteLine(name);
}
}
}
USBDeviceInfo Class
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)
Debug.WriteLine(childDeviceId.Replace(#"\", #"\\") );
foreach (ManagementObject drive in new ManagementObjectSearcher("SELECT DeviceID FROM Win32_DiskDrive WHERE PNPDeviceID='" + childDeviceId.Replace(#"\", #"\\") + "'").Get())
{
foreach (PropertyData usb in drive.Properties){
if (usb.Value != null && usb.Value.ToString() != "")
{
Debug.Write(usb.Name + "=");
Debug.Write(usb.Value + "\r\n");
}
}
// 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"];
}
}
}
}
}
}
As a side note, I am able to get the Query using "Model" property pass and then find the drive letter, as explained here. But I'm looking for a solution that can tie VID/PID to the drive letter.
I had exactly the same problem and after a hard week of work I finally got the solution.
I also get two links for one device (the link with the vid and pid and a link, which contains "USBSTOR...."). I think it's not the best way to solve the problem, but (until now) it works.
I used two functions:
The first one is to find a USBHub with a specific VID and PID and also find the related second link ("USBSTOR...."). This link is important, because it forms the connection to the drive letter.
A list named "USBobjects" contains a number of related links (USBHub, USBSTOR,....), which refer to all attached devices. I found out, that the USBSTOR link appears right after the link, which contains the VID and PID.
I stored the "USBStOR..." link as a string and used it for the second function to find the related PNPEntity of the DiskDrive. This leads to the correct DiskPartition and furthermore to the LogicalDisk = drive letter.
Hopefully it's not to late and both functions will help u!
FIRST FUNCTION:
public void FindPath()
{
foreach (ManagementObject entity in new ManagementObjectSearcher("select * from Win32_USBHub Where DeviceID Like '%VID_XXXX&PID_XXXX%'").Get())
{
Entity = entity["DeviceID"].ToString();
foreach (ManagementObject controller in entity.GetRelated("Win32_USBController"))
{
foreach (ManagementObject obj in new ManagementObjectSearcher("ASSOCIATORS OF {Win32_USBController.DeviceID='" + controller["PNPDeviceID"].ToString() + "'}").Get())
{
if(obj.ToString().Contains("DeviceID"))
USBobjects.Add(obj["DeviceID"].ToString());
}
}
}
int VidPidposition = USBobjects.IndexOf(Entity);
for (int i = VidPidposition; i <= USBobjects.Count; i++ )
{
if (USBobjects[i].Contains("USBSTOR"))
{
Secondentity = USBobjects[i];
break;
}
}
}
>
SECOND FUNCTION:
public void GetDriveLetter()
{
foreach (ManagementObject drive in new ManagementObjectSearcher("select * from Win32_DiskDrive").Get())
{
if (drive["PNPDeviceID"].ToString() == Secondentity)
{
foreach (ManagementObject o in drive.GetRelated("Win32_DiskPartition"))
{
foreach (ManagementObject i in o.GetRelated("Win32_LogicalDisk"))
{
Console.WriteLine("Disk: " + i["Name"].ToString());
}
}
}
}
}
>
many thanks to #Michaela for his very helpful answer, but I think this code is shorter
public void FindPath()
{
ManagementObjectSearcher entity = new ManagementObjectSearcher("SELECT * FROM Win32_DiskDrive");
foreach (ManagementObject obj in entity.Get())
{
if (obj["PNPDeviceID"].ToString().Contains("USBSTOR"))
{
if (!USBobjects.Contains(obj["PNPDeviceID"].ToString()))
USBobjects.Add(obj["PNPDeviceID"].ToString());
}
}
}
I have another problem with GetDriveLetter() method, sometimes it takes too long to compile foreach line. can somebody tell me the reason?
DriveInfo[] ListDrives = DriveInfo.GetDrives();
foreach (DriveInfo Drive in ListDrives)
{
if (Drive.DriveType == DriveType.Removable)
{
try
{
Console.WriteLine(Drive.Name);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
As following i able to get usb com port names attached to 32bit win7OS machine, by given pid and vid,but when running in x64 it stuck in the following line:
comports.Add((string)rk6.GetValue("PortName"));
This is my code
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;
}
actual code get here, So how to get com port names in x64, any suggestion?
While I was testing the answer from Youkko under Windows 10 x64 I was getting some strange results and looking at the registry on my machine the LocationInformation keys contained strings such as Port_#0002.Hub_#0003so they are related to the USB hub / port the device is connected to not the COM port allocated by Windows.
So in my case I was getting COM2 included which is a hardware port on my motherboard and it skipped the COM5 port I was expecting but that was located under the PortName registry key. I'm not sure if something has changed since the version of Windows you were using but I think your main problem might have been not checking for null values on the keys.
The following slightly modified version seems to work fine on a variety or Windows 7 / 10 and x32 / 64 systems and I've also added a to check of SerialPort.GetPortNames() to make sure the device is available and plugged into the system before returning it:
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;
}
By reading your code, I found out that the current path you're looking at in registry does not contain any information about ports.
But I found a way to read it by doing this little change:
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;
}
It did work perfectly.
Thank you for your code, by the way... It helped me a lot!
Here is my take on this (even if it is not a direct A to the Q)
USB devices is always (and has always) enumerated under HKLM\SYSTEM\CurrentControlSet\Enum\USB (note USB at the end)
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"
Currently available com ports is listed by System.IO.Ports.SerialPort.GetPortNames()
OpenSubKey implements IDisposable and should have using or .Dispose() on them
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);
}
}
Ok, using ManagementObjectSearcher (it gives COM-port index and VID and PID if they exist):
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);
}
I think ManagementObjectSearcher may be a better approach than directly reading the registry.
Here's an example for virtual COM ports.
I'm trying to make a function that detects if a usb device is connected given the devices pid and vid. I'm hoping it would look something like this, I'm just not sure how to do this in C#.
public bool IsUsbDeviceConnected(string pid, string vid)
{
//Code here
}
//using System.Management
public bool IsUsbDeviceConnected(string pid, string vid)
{
using (var searcher =
new ManagementObjectSearcher(#"Select * From Win32_USBControllerDevice"))
{
using (var collection = searcher.Get())
{
foreach (var device in collection)
{
var usbDevice = Convert.ToString(device);
if (usbDevice.Contains(pid) && usbDevice.Contains(vid))
return true;
}
}
}
return false;
}
may be something like
//import the System.Management namespace at the top in your "using" statement. Then in a method, or on a button click:
ManagementObjectCollection collection;
using (ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT * FROM Win32_DiskDrive WHERE InterfaceType='USB'"))
collection = searcher.Get();
foreach (ManagementObject currentObject in collection)
{
ManagementObject theSerialNumberObjectQuery = new ManagementObject("Win32_PhysicalMedia.Tag='" + currentObject["DeviceID"] + "'");
MessageBox.Show(theSerialNumberObjectQuery["SerialNumber"].ToString());
}
collection.Dispose();
Using WMI