2010-05-30 96 views
24

友好名称=出现在“设备管理器”下的“端口名称(COM & LPT)如何通过友好名称打开串口?

编辑:。两种解决方案低一个提供WMI,另一个SETUPAPI

+0

像“COM1”,“COM2”等?或“Aten USB Serial(COM1)”。如果前者然后SerialPort1.Portname =“COM1”那么SerialPort1.open – dbasnett 2010-05-30 10:41:23

+0

后者,不幸的是 – 2010-05-30 13:09:42

+1

WMI - 两个查询在“root \ CIMV2”。 1)“SELECT * FROM Win32_SerialPort”或2)“SELECT * FROM Win32_PnPEntity WHERE Con​​figManagerErrorCode = 0”。正如所建议的使用WMICodeCreator的具体细节。查询结果将需要扫描。 – dbasnett 2010-05-31 12:18:17

回答

0

尝试运行在Win32_SerialPort类WMI查询下载WmiCodeCreator进行实验,并自动生成的C#代码

+2

我不知道为什么,但在我的使用WMI的系统上找不到COM1以外的任何东西。即使指定一个已知的端口,例如'Select * from Win32_SerialPort',其中Name ='COM11'也不会返回任何内容。尽管如此,setupapi解决方案仍然完美。任何想法为什么WMI只知道COM1? – 2011-02-02 12:10:19

+0

@蒂姆 - 从来没有听说过这个失败,不知道。 – 2011-02-02 12:34:14

+1

@Hans Passant:根据此[链接](http://www.creativecodedesign.com/node/39),使用WMI查询“SELECT * FROM Win32_SerialPort”并不总是返回所有COM端口。该链接提供了另一种解决方案。 – markyd13 2012-04-25 19:53:42

25

发帖今晚的代码,每个人的享受:。

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


} 
+2

我只想回来说我一直在使用这个代码废话。最近有许多项目与串行设备一起工作。谢谢! – 2011-04-12 19:35:37

+0

感谢你,而不是迟来的。 – Robinson 2012-03-23 11:55:44

+0

我无法得到这个示例工作...应该作为friendlyNamePrefix传入什么?我尝试过传入端口名称,但它总是返回null :( – NickG 2012-12-19 11:48:55

5

我知道这是张贴在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和蓝牙的。

11

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

这工作完美!感谢您重新发布! – 2014-08-12 17:28:42

0

您还可能要为我发现WMI很慢(5或6秒)考虑使用注册表

在我的案例我想识别具有已知友好名称的设备的COM端口名称。使用regedit我搜索注册表中的友好名称在一个密钥也包含COM端口。因为我找到的密钥是某种随机ID以及其他10个密钥,所以我上了几个级别以找到适合在其中搜索的密钥。

我想出的代码如下:

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 
2

帕维尔的SetupDiWrap类的伟大工程,它只是需要为Windows 7

稍微调整希望此更新能够帮助其他人挣扎(像我一样)在Windows 7中从VCP名称中获取COM端口号。

1)SP_DEVINFO_DATA已在Windows 7中更改,总长度不再是28字节,它是长度为32个字节。这对我很有用:

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代表保留,而不是int。将uint cbSize更改为int cbSize稍后为我保留了演员表,否则您可以将其保留为uint。

2)I也写行:

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

有点不同,为清楚起见,以获得CBSIZE到32位:

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

3)I改变

[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

代码现在在Windows 7中像魅力一样运行!

+0

你为什么不编辑我的答案?让我们让人们去一个地方。 – 2013-12-14 03:17:51

-2

我使用com0com生成的虚拟端口。 有了您能够为要命名的端口 - 所以我口字幕喜欢如下: "com0com - bus for serial port pair emulator 0 (COMA <-> COMB)"

有了上面贴WMI代码,那么你将得到"COMA <-> COMB"该端口。 似乎这样的结构是没有端口,但上面的代码将它视为串口...

顺便说一句,"COMA"是一个完全有效的名称为端口...(这意味着,只查找数字在结局不够)。

所以我不知道我怎么能够可靠有效的,现有的串行端口名称,这样奇特的结构区分...

+0

为什么你设置“答案没用”? – Mich0815 2015-01-29 10:57:59

+0

Mich0815 - 我怀疑,因为这只是给你另一个假端口和名称,以中继到未知的端口。他仍然会遇到需要自动发现实际设备端口以便首先设置该地图的问题。 – etropic 2016-01-22 19:02:58

0

如果你是专门使用的USB设备,而不是一些其他类型的COM端口,交叉平台USB库012​​有一个example您可以运行,显示如何根据COM端口的USB产品ID和供应商ID查找COM端口名称。

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