web-dev-qa-db-fra.com

Comment ouvrir un port série par nom convivial?

nom convivial = le nom qui apparaît dans "Gestionnaire de périphériques" sous "Ports (COM & LPT).

EDIT: deux solutions fournies ci-dessous. Un avec WMI et un autre avec SetupAPI.

24

Essayez d'exécuter une requête WMI sur la classe Win32_SerialPort. Download WmiCodeCreator pour expérimenter et générer automatiquement le code C #.

0
Hans Passant

Afficher le code de ce soir, pour le plus grand plaisir de tous:

public class SetupDiWrap
{
    static public string ComPortNameFromFriendlyNamePrefix(string friendlyNamePrefix)
    {
        const string className = "Ports";
        Guid[] guids = GetClassGUIDs(className);

        System.Text.RegularExpressions.Regex friendlyNameToComPort =
            new System.Text.RegularExpressions.Regex(@".? \((COM\d+)\)$");  // "..... (COMxxx)" -> COMxxxx

        foreach (Guid guid in guids)
        {
            // We start at the "root" of the device tree and look for all
            // devices that match the interface GUID of a disk
            Guid guidClone = guid;
            IntPtr h = SetupDiGetClassDevs(ref guidClone, IntPtr.Zero, IntPtr.Zero, DIGCF_PRESENT | DIGCF_PROFILE);
            if (h.ToInt32() != INVALID_HANDLE_VALUE)
            {
                int nDevice = 0;
                while (true)
                {
                    SP_DEVINFO_DATA da = new SP_DEVINFO_DATA();
                    da.cbSize = (uint)Marshal.SizeOf(da);

                    if (0 == SetupDiEnumDeviceInfo(h, nDevice++, ref da))
                        break;

                    uint RegType;
                    byte[] ptrBuf = new byte[BUFFER_SIZE];
                    uint RequiredSize;
                    if (SetupDiGetDeviceRegistryProperty(h, ref da,
                        (uint)SPDRP.FRIENDLYNAME, out RegType, ptrBuf,
                        BUFFER_SIZE, out RequiredSize))
                    {
                        const int utf16terminatorSize_bytes = 2;
                        string friendlyName = System.Text.UnicodeEncoding.Unicode.GetString(ptrBuf, 0, (int)RequiredSize - utf16terminatorSize_bytes);

                        if (!friendlyName.StartsWith(friendlyNamePrefix))
                            continue;

                        if (!friendlyNameToComPort.IsMatch(friendlyName))
                            continue;

                        return friendlyNameToComPort.Match(friendlyName).Groups[1].Value;
                    }
                } // devices
                SetupDiDestroyDeviceInfoList(h);
            }
        } // class guids

        return null;
    }

    /// <summary>
    /// The SP_DEVINFO_DATA structure defines a device instance that is a member of a device information set.
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    private struct SP_DEVINFO_DATA
    {
        /// <summary>Size of the structure, in bytes.</summary>
        public uint cbSize;
        /// <summary>GUID of the device interface class.</summary>
        public Guid ClassGuid;
        /// <summary>Handle to this device instance.</summary>
        public uint DevInst;
        /// <summary>Reserved; do not use.</summary>
        public uint Reserved;
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct SP_DEVICE_INTERFACE_DATA
    {
        public Int32 cbSize;
        public Guid interfaceClassGuid;
        public Int32 flags;
        private UIntPtr reserved;
    }

    const int BUFFER_SIZE = 1024;

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    private struct SP_DEVICE_INTERFACE_DETAIL_DATA
    {
        public int cbSize;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = BUFFER_SIZE)]
        public string DevicePath;
    }

    private enum SPDRP
    {
        DEVICEDESC = 0x00000000,
        HARDWAREID = 0x00000001,
        COMPATIBLEIDS = 0x00000002,
        NTDEVICEPATHS = 0x00000003,
        SERVICE = 0x00000004,
        CONFIGURATION = 0x00000005,
        CONFIGURATIONVECTOR = 0x00000006,
        CLASS = 0x00000007,
        CLASSGUID = 0x00000008,
        DRIVER = 0x00000009,
        CONFIGFLAGS = 0x0000000A,
        MFG = 0x0000000B,
        FRIENDLYNAME = 0x0000000C,
        LOCATION_INFORMATION = 0x0000000D,
        PHYSICAL_DEVICE_OBJECT_NAME = 0x0000000E,
        CAPABILITIES = 0x0000000F,
        UI_NUMBER = 0x00000010,
        UPPERFILTERS = 0x00000011,
        LOWERFILTERS = 0x00000012,
        MAXIMUM_PROPERTY = 0x00000013,
    }

    [DllImport("setupapi.dll", SetLastError = true)]
    static extern bool SetupDiClassGuidsFromName(string ClassName,
        ref Guid ClassGuidArray1stItem, UInt32 ClassGuidArraySize,
        out UInt32 RequiredSize);

    [DllImport("setupapi.dll")]
    internal static extern IntPtr SetupDiGetClassDevsEx(IntPtr ClassGuid,
        [MarshalAs(UnmanagedType.LPStr)]String enumerator,
        IntPtr hwndParent, Int32 Flags, IntPtr DeviceInfoSet,
        [MarshalAs(UnmanagedType.LPStr)]String MachineName, IntPtr Reserved);

    [DllImport("setupapi.dll")]
    internal static extern Int32 SetupDiDestroyDeviceInfoList(IntPtr DeviceInfoSet);

    [DllImport(@"setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern Boolean SetupDiEnumDeviceInterfaces(
       IntPtr hDevInfo,
       IntPtr optionalCrap, //ref SP_DEVINFO_DATA devInfo,
       ref Guid interfaceClassGuid,
       UInt32 memberIndex,
       ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData
    );

    [DllImport("setupapi.dll")]
    private static extern Int32 SetupDiEnumDeviceInfo(IntPtr DeviceInfoSet,
        Int32 MemberIndex, ref SP_DEVINFO_DATA DeviceInterfaceData);

    [DllImport("setupapi.dll")]
    private static extern Int32 SetupDiClassNameFromGuid(ref Guid ClassGuid,
        StringBuilder className, Int32 ClassNameSize, ref Int32 RequiredSize);

    [DllImport("setupapi.dll")]
    private static extern Int32 SetupDiGetClassDescription(ref Guid ClassGuid,
        StringBuilder classDescription, Int32 ClassDescriptionSize, ref Int32 RequiredSize);

    [DllImport("setupapi.dll")]
    private static extern Int32 SetupDiGetDeviceInstanceId(IntPtr DeviceInfoSet,
        ref SP_DEVINFO_DATA DeviceInfoData,
        StringBuilder DeviceInstanceId, Int32 DeviceInstanceIdSize, ref Int32 RequiredSize);

    [DllImport("setupapi.dll", CharSet = CharSet.Auto)]
    static extern IntPtr SetupDiGetClassDevs(           // 1st form using a ClassGUID only, with null Enumerator
       ref Guid ClassGuid,
       IntPtr Enumerator,
       IntPtr hwndParent,
       int Flags
    );

    [DllImport(@"setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern Boolean SetupDiGetDeviceInterfaceDetail(
       IntPtr hDevInfo,
       ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData,
       ref SP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData,
       UInt32 deviceInterfaceDetailDataSize,
       out UInt32 requiredSize,
       ref SP_DEVINFO_DATA deviceInfoData
    );

    /// <summary>
    /// The SetupDiGetDeviceRegistryProperty function retrieves the specified device property.
    /// This handle is typically returned by the SetupDiGetClassDevs or SetupDiGetClassDevsEx function.
    /// </summary>
    /// <param Name="DeviceInfoSet">Handle to the device information set that contains the interface and its underlying device.</param>
    /// <param Name="DeviceInfoData">Pointer to an SP_DEVINFO_DATA structure that defines the device instance.</param>
    /// <param Name="Property">Device property to be retrieved. SEE MSDN</param>
    /// <param Name="PropertyRegDataType">Pointer to a variable that receives the registry data Type. This parameter can be NULL.</param>
    /// <param Name="PropertyBuffer">Pointer to a buffer that receives the requested device property.</param>
    /// <param Name="PropertyBufferSize">Size of the buffer, in bytes.</param>
    /// <param Name="RequiredSize">Pointer to a variable that receives the required buffer size, in bytes. This parameter can be NULL.</param>
    /// <returns>If the function succeeds, the return value is nonzero.</returns>
    [DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern bool SetupDiGetDeviceRegistryProperty(
        IntPtr DeviceInfoSet,
        ref SP_DEVINFO_DATA DeviceInfoData,
        uint Property,
        out UInt32 PropertyRegDataType,
        byte[] PropertyBuffer,
        uint PropertyBufferSize,
        out UInt32 RequiredSize);


    const int DIGCF_DEFAULT = 0x1;
    const int DIGCF_PRESENT = 0x2;
    const int DIGCF_ALLCLASSES = 0x4;
    const int DIGCF_PROFILE = 0x8;
    const int DIGCF_DEVICEINTERFACE = 0x10;
    const int INVALID_HANDLE_VALUE = -1;

    private static Guid[] GetClassGUIDs(string className)
    {
        UInt32 requiredSize = 0;
        Guid[] guidArray = new Guid[1];

        bool status = SetupDiClassGuidsFromName(className, ref guidArray[0], 1, out requiredSize);
        if (true == status)
        {
            if (1 < requiredSize)
            {
                guidArray = new Guid[requiredSize];
                SetupDiClassGuidsFromName(className, ref guidArray[0], requiredSize, out requiredSize);
            }
        }
        else
            throw new System.ComponentModel.Win32Exception();

        return guidArray;
    }


}
25

Le code de cet article a fait le travail pour moi (il a un lien vers ce post, mais il ne semble pas avoir fourni de réponse ici lui-même). Voici le code de l'auteur:

using System.Management;
internal class ProcessConnection { 

   public static ConnectionOptions ProcessConnectionOptions()
   {
     ConnectionOptions options = new ConnectionOptions();
     options.Impersonation = ImpersonationLevel.Impersonate;
     options.Authentication = AuthenticationLevel.Default;
     options.EnablePrivileges = true;
     return options;
   }

   public static ManagementScope ConnectionScope(string machineName, ConnectionOptions options, string path)
   {
     ManagementScope connectScope = new ManagementScope();
     connectScope.Path = new ManagementPath(@"\\" + machineName + path);
     connectScope.Options = options;
     connectScope.Connect();
     return connectScope;
   }
}

public class COMPortInfo
{
   public string Name { get; set; }
   public string Description { get; set; }

   public COMPortInfo() { }     

   public static List<COMPortInfo> GetCOMPortsInfo()
   {
     List<COMPortInfo> comPortInfoList = new List<COMPortInfo>();

     ConnectionOptions options = ProcessConnection.ProcessConnectionOptions();
     ManagementScope connectionScope = ProcessConnection.ConnectionScope(Environment.MachineName, options, @"\root\CIMV2");

     ObjectQuery objectQuery = new ObjectQuery("SELECT * FROM Win32_PnPEntity WHERE ConfigManagerErrorCode = 0");
     ManagementObjectSearcher comPortSearcher = new ManagementObjectSearcher(connectionScope, objectQuery);

     using (comPortSearcher)
     {
       string caption = null;
       foreach (ManagementObject obj in comPortSearcher.Get())
       {
         if (obj != null)
         {
           object captionObj = obj["Caption"];
           if (captionObj != null)
           {
              caption = captionObj.ToString();
              if (caption.Contains("(COM"))
              {
                COMPortInfo comPortInfo = new COMPortInfo();
                comPortInfo.Name = caption.Substring(caption.LastIndexOf("(COM")).Replace("(", string.Empty).Replace(")",
                                                     string.Empty);
                comPortInfo.Description = caption;
                comPortInfoList.Add(comPortInfo);
              }
           }
         }
       }
     } 
     return comPortInfoList;
   } 
}

Usage:

foreach (COMPortInfo comPort in COMPortInfo.GetCOMPortsInfo())
{
  Console.WriteLine(string.Format("{0} – {1}", comPort.Name, comPort.Description));
}
11
Pat

Je sais que cela a été posté en C #, mais je suis certain que cela peut facilement être converti ...

    Public Function foo() As Integer
    Try
        Dim searcher As New ManagementObjectSearcher( _
            "root\CIMV2", _
            "SELECT * FROM Win32_SerialPort")

        For Each queryObj As ManagementObject In searcher.Get()
            Debug.WriteLine(queryObj("Caption"))
            Debug.WriteLine(queryObj("Description"))
            Debug.WriteLine(queryObj("DeviceID"))
            Debug.WriteLine(queryObj("Name"))
            Debug.WriteLine(queryObj("PNPDeviceID"))

        Next
    Catch err As ManagementException
        Stop
    End Try
End Function

Public Function bar() As Integer
    Try
        Dim searcher As New ManagementObjectSearcher( _
            "root\CIMV2", _
            "SELECT * FROM Win32_PnPEntity WHERE ConfigManagerErrorCode = 0")

        For Each queryObj As ManagementObject In searcher.Get()
            If queryObj("Caption").ToString.Contains("(COM") Then
                Debug.WriteLine(queryObj("Caption"))
                Debug.WriteLine(queryObj("Description"))
                Debug.WriteLine(queryObj("DeviceID"))
                Debug.WriteLine(queryObj("Name"))
                Debug.WriteLine(queryObj("PNPDeviceID"))
            End If
        Next
    Catch err As ManagementException
        Stop
    End Try
End Function

Il trouve tous mes ports com, modem, série, usb et bluetooth.

5
dbasnett

La classe SetupDiWrap de Pavel fonctionne très bien, il ne lui faut que quelques petites modifications pour Windows 7. 

Espérons que cette mise à jour aidera d'autres personnes (comme moi) à obtenir les numéros de port COM à partir des noms VCP dans Windows 7.

1) Le SP_DEVINFO_DATA a changé dans Windows 7, la longueur totale n’est plus de 28 octets, mais de 32 octets. C'est ce qui fonctionne pour moi:

   private struct SP_DEVINFO_DATA
        {
            /// <summary>Size of the structure, in bytes.</summary>
            public int cbSize;
            /// <summary>GUID of the device interface class.</summary>
            public Guid ClassGuid;
            /// <summary>Handle to this device instance.</summary>
            public int DevInst;
            /// <summary>Reserved; do not use.</summary>
            public ulong Reserved;
        }

Notez ulong pour Reserved au lieu d'un int. Changer uint cbSize en int cbSize m'a sauvé un casting plus tard, sinon vous pouvez le laisser comme uint.

2) J'ai aussi écrit la ligne:

 da.cbSize = (uint)Marshal.SizeOf(da);

un peu différent, par souci de clarté, pour obtenir le cbSize à 32 bits:

da.cbSize = Marshal.SizeOf(typeof(SP_DEVINFO_DATA));

3) j'ai changé

 [DllImport("setupapi.dll", CharSet = CharSet.Auto)]
        static extern IntPtr SetupDiGetClassDevs(       
           ref Guid ClassGuid,
           IntPtr Enumerator,
           IntPtr hwndParent,
           int Flags
        );

à

 [DllImport("setupapi.dll", CharSet = CharSet.Auto)]
        private static extern IntPtr SetupDiGetClassDevs( 
           ref Guid ClassGuid,
           UInt32 Enumerator,
           IntPtr hwndParent,
           UInt32 Flags
        );

L'énumérateur n'est plus une IntPtr, vous devez donc appeler SetupDiGetClassDevs comme ceci:

IntPtr h = SetupDiGetClassDevs(ref guidClone, 0, IntPtr.Zero, DIGCF_PRESENT | DIGCF_PROFILE);

Notez "0" au lieu de IntPtr.Zero lors du passage de l'énumérateur.

Le code fonctionne maintenant comme un charme dans Windows 7!

2
vardelean

Si vous utilisez spécifiquement des périphériques USB et non un autre type de port COM, la bibliothèque multi-plateforme USB libusbp a un exemple vous pouvez exécuter qui indique comment trouver le nom du port COM à partir du port USB. ID produit et ID fournisseur du port COM.

C’est un choix différent mais probablement meilleur que d’essayer d’utiliser le nom convivial dans le Gestionnaire de périphériques. Peut-être que libusbp pourrait être étendu pour permettre l'accès au nom convivial si vous le souhaitez vraiment.

0
David Grayson

Vous pouvez également envisager d’utiliser le registre car j’ai trouvé WMI assez lent (5 ou 6 secondes).

Dans mon cas, je voulais identifier le nom du port COM d'un périphérique avec un nom convivial connu. En utilisant regedit, j'ai recherché dans le registre le nom convivial d'une clé contenant également le port COM. Parce que la clé que j'ai trouvée était une sorte d'identifiant aléatoire avec 10 autres, j'ai gravi quelques niveaux pour trouver une clé appropriée pour la recherche à l'intérieur.

Le code que j'ai créé est le suivant:

Dim searchFriendlyName = "Your Device Name".ToLower
Dim k0 = Registry.LocalMachine.OpenSubKey("SYSTEM\CurrentControlSet\Enum\USB\", False)
For Each k1Name In k0.GetSubKeyNames
    Dim k1 = k0.OpenSubKey(k1Name, False)
    For Each k2name In k1.GetSubKeyNames
        Dim k2 = k1.OpenSubKey(k2name, False)
        If k2.GetValueNames.Contains("FriendlyName") AndAlso k2.GetValue("FriendlyName").ToString.ToLower.Contains(searchFriendlyName) Then
            If k2.GetSubKeyNames.Contains("Device Parameters") Then
                Dim k3 = k2.OpenSubKey("Device Parameters", False)
                If k3.GetValueNames.Contains("PortName") Then
                    For Each s In SerialPort.GetPortNames
                        If k3.GetValue("PortName").ToString.ToLower = s.ToLower Then
                            Return s
                        End If
                    Next
                End If
            End If
        End If
    Next
Next

Cela devra bien sûr être modifié en fonction du lieu et de l'emplacement de votre appareil dans la base de registres, mais si vous essayez de "détecter automatiquement" le port de communication d'un type de compte spécifique, vous devriez pouvoir le faire.

N'oubliez pas que si vous devez rechercher de manière récurrente un grand nombre de clés, la solution ci-dessus sera ralentie. Essayez donc de trouver le bon emplacement dans le registre.

J'ai également inclus le code WMI après la recherche dans le registre au cas où la recherche dans le registre serait vide:

Dim mg As New System.Management.ManagementClass("Win32_SerialPort")
Try

    For Each i In mg.GetInstances()
        Dim name = i.GetPropertyValue("Name")
        If name IsNot Nothing AndAlso name.ToString.ToLower.Contains(searchFriendlyName.ToLower) Then
            Return i.GetPropertyValue("DeviceId").ToString
        End If
    Next
Finally
    mg.Dispose()
End Try
0
apc