代码之家  ›  专栏  ›  技术社区  ›  Pavel Radzivilovsky

如何用友好名称打开串口?

  •  24
  • Pavel Radzivilovsky  · 技术社区  · 16 年前

    编辑:下面提供了两种解决方案。一个使用WMI,另一个使用SetupAPI。

    8 回复  |  直到 16 年前
        1
  •  0
  •   Hans Passant    16 年前

    尝试在Win32\U SerialPort类上运行WMI查询。下载 WmiCodeCreator

        2
  •  25
  •   Pavel Radzivilovsky    16 年前

    张贴今晚的密码,供大家欣赏:

    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;
        }
    
    
    }
    
        3
  •  11
  •   Pat    15 年前

    This article 的代码为我做了这项工作(他链接到这篇文章,但似乎没有提供一个答案在这里自己)。以下是作者代码:

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

    用法:

    foreach (COMPortInfo comPort in COMPortInfo.GetCOMPortsInfo())
    {
      Console.WriteLine(string.Format("{0} – {1}", comPort.Name, comPort.Description));
    }
    
        4
  •  5
  •   dbasnett    16 年前

    我知道这是用C#发布的,但我确信这可以很容易地转换。。。

        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
    

    它可以找到我的所有com端口、调制解调器、串行、usb和蓝牙。

        5
  •  2
  •   Erti-Chris Eelmaa    11 年前

    帕维尔的课 SetupDiWrap

    希望这个更新能帮助其他人(像我一样)从Windows7中的VCP名称中获取COM端口号。

    1) 那个 SP_DEVINFO_DATA

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

    注意ulong代表Reserved,而不是int.Changing uint cbSize 到int

    2) 我还写了一句话:

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

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

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

    枚举器不再是 IntPtr ,所以你需要打电话 SetupDiGetClassDevs 这样地:

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

    注意“0”,而不是 IntPtr.Zero

    这个代码现在在Windows7中运行起来很有魅力!

        6
  •  0
  •   apc    12 年前

    您可能还想考虑使用注册表,因为我发现WMI非常慢(5或6秒)

    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
    

    这当然需要根据您的设备在注册表中显示的方式和位置进行修改,但是如果您尝试“自动检测”特定类型设备的Com端口,那么您应该能够使其正常工作。

    请记住,如果您必须递归搜索大量的键,那么这将减慢上述解决方案,所以请尝试在注册表中找到正确的位置来查找。

    我还在注册表搜索之后加入了WMI代码,以防注册表搜索结果为空:

    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
    
        7
  •  0
  •   David Grayson    9 年前

    如果您是专门使用USB设备而不是其他类型的COM端口,那么跨平台USB库 libusbp 有一个 example

    与在设备管理器中尝试使用友好名称相比,这是一个不同但可能更好的选择。如果您真的需要,也许可以扩展libusbp以允许访问友好名称。

        8
  •  -2
  •   Mich0815    11 年前

    com0com . 因此,您可以根据需要命名端口-因此我有如下端口标题: "com0com - bus for serial port pair emulator 0 (COMA <-> COMB)"

    使用上面发布的WMI代码,您将获得 "COMA <-> COMB" 去那个港口。 似乎这样的构造没有端口,但是上面的代码将把它当作串行端口。。。

    顺便说一句, "COMA" 是端口的完全有效名称(这意味着,只在末尾寻找数字是不够的)。