2012-04-27 18 views
6

Come segue sono in grado di ottenere nomi porta com USB collegati a macchina win7OS a 32 bit, dato pid e vid, ma quando è in esecuzione in x64 si blocca nella riga seguente:Identifica la porta COM usando VID e PID per il dispositivo USB collegato a x64

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

Questo è il mio codice

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

codice vero e ottenere here, così come ottenere i nomi delle porte COM in x64, qualsiasi suggerimento?

+0

Questo è inimmaginabile, almeno è necessaria una traccia dello stack. –

+0

questo metodo restituisce l'elenco di porte com che collegano il nostro dispositivo ex: COM3, COM4 ecc., Problema non funziona su x64 – UdayaLakmal

+1

Dal momento che potrebbe essere rilevante per gli altri che vengono qui: La semplice ragione è probabilmente dovuta a 'rk6' essendo 'null' dato che' 'Parametri dispositivo '' La chiave non esiste su ogni nodo del dispositivo e come tale non è correlata alla versione a 32 bit o 64 bit di OS – NiKiZe

risposta

6

Leggendo il tuo codice, ho scoperto che il percorso corrente che stai guardando nel registro non contiene alcuna informazione sulle porte. Ma ho trovato un modo per leggerlo facendo questo piccolo cambiamento:

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

E ha funzionato perfettamente. Grazie per il tuo codice, a proposito ... mi ha aiutato molto!

+1

Inoltre, utilizzare "if (! String.IsNullOrEmpty (posizione))" invece di "if (location! = String.Empty)" ... Ho trovato che la posizione può essere nullo. – dlchambers

+0

'LocationInformation' è il percorso hardware del dispositivo USB, _not_ numero di porta COM – NiKiZe

2

Penso che ManagementObjectSearcher potrebbe essere un approccio migliore rispetto alla lettura diretta del registro.

Ecco lo an example per le porte COM virtuali.

+0

Per aggiungere a ciò, provare a scaricare WMI Code Creator https://www.microsoft.com/en-us/download/confirmation.aspx?id=8572. Impostare lo spazio dei nomi su "root \ WMI" e le classi su "MSSerial_PortName", quindi dovresti essere in grado di modificare il ricercatore di ManagementObjectSearcher per richiedere che 'InstanceName' (le informazioni USB per una porta com) contenga una stringa particolare (ad esempio " PID_7523 "). Se ottengo un esempio funzionante, lo metterò come risposta più avanti. – drojf

+0

Sembra che ci siano vari problemi con questo metodo (ad esempio, Windows non inserisce sempre il numero della porta com nella descrizione, errori di accesso vengono negati quando si utilizza root \ WMI/a titolo definitivo nessuna porta COM visualizzata su win10). Il metodo di PeterJ sembra il miglior sofar. – drojf

2

Mentre stavo testando la risposta da Youkko sotto Windows 10 x64 stavo ottenendo alcuni risultati strani e guardando il Registro di sistema sulla mia macchina le chiavi LocationInformation contenevano stringhe quali Port_#0002.Hub_#0003 in modo che siano collegate all'hub USB/porto il dispositivo è collegato a non la porta COM assegnata da Windows.

Quindi nel mio caso stavo ricevendo COM2 incluso che è una porta hardware sulla mia scheda madre e ha saltato la porta COM5 che mi aspettavo ma che si trovava sotto la chiave di registro PortName. Non sono sicuro se qualcosa è cambiato dalla versione di Windows che stavi usando, ma penso che il tuo problema principale potrebbe non aver controllato i valori nulli sulle chiavi.

La seguente versione leggermente modificata sembra funzionare correttamente su una varietà o sistemi Windows 7/10 e x32/64 e ho anche aggiunto un controllo a SerialPort.GetPortNames() per assicurarsi che il dispositivo sia disponibile e collegato al sistema prima riportandolo:

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

Al momento in cui ho risposto, anche Windows 8 non esisteva. La mia risposta funziona con Windows 7. Ma grazie mille per questo contributo. Ogni risposta ha bisogno di aggiornamenti a volte :-) – Youkko

1

Ecco il mio prendere su questo (anche se non è un a diretto al Q)

  • dispositivi USB è sempre (e da sempre) enumerati sotto HKLM\SYSTEM\CurrentControlSet\Enum\USB (nota USB alla fine)
    • nodi di dispositivo hanno il formato VID_xxxx&PID_xxxx* dove xxxx è esadecimale, ci potrebbero essere alcuni dati funzione supplementari alla fine
    • Ogni nodo dispositivo ha un nodo secondario identificatore basato su serialnumber o altri dati del dispositivo e funzioni nodo
    • identificatore può avere valore "FriendlyName", che a volte hanno il COM in parantheses come "Virtual Serial Port (COM6)"
    • percorso risultante: HKLM\SYSTEM\CurrentControlSet\Enum\USB\VID_xxxx&PID_xxxx*\*\Device Parameters\ ha valore denominato "PortName"
  • attualmente disponibili porte COM è elencata da System.IO.Ports.SerialPort.GetPortNames()
  • OpenSubKey implementa IDisposable e dovrebbe avere using o .Dispose() su di loro

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

Ok, utilizzando ManagementObjectSearcher (Dà indice di COM-port e VID e PID se esistono):

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