代码之家  ›  专栏  ›  技术社区  ›  Richard C

如何在Windows中获取COM端口的友好名称?

  •  14
  • Richard C  · 技术社区  · 16 年前

    第二个端口用于从调制解调器获取重要信息,如信号质量;发送和接收短信;还有很多其他的功能。

    我正在编写一个应用程序,它将封装第二个端口提供的一些功能。我需要的是确定哪个COM端口是备用端口的可靠方法。迭代端口并检查对“ATE0”的响应是不够的。调制解调器的端口通常是编号较低的端口,当拨号连接未激活时,它将与第二个端口一样响应“ATE0”。

    我想做的是迭代端口并检查它们的友好名称,如设备管理器中所示。这样,我可以将应用程序中的端口链接到设备管理器中标记为“华为移动连接-3G PC UI接口(COM6)”的端口。我只是还没有找到任何可以让我以编程方式获取该名称的信息。

    6 回复  |  直到 16 年前
        1
  •  8
  •   Will Dean    16 年前

    很久以前,我为客户端编写了一个实用程序来实现这一点,但它是为GPS而不是调制解调器编写的。

    我刚刚看了一下,可能有帮助的地方有:

        GUID guid = GUID_DEVCLASS_PORTS;
    
    SP_DEVICE_INTERFACE_DATA interfaceData;
    ZeroMemory(&interfaceData, sizeof(interfaceData));
    interfaceData.cbSize = sizeof(interfaceData);
    
    SP_DEVINFO_DATA devInfoData;
    ZeroMemory(&devInfoData, sizeof(devInfoData));
    devInfoData.cbSize = sizeof(devInfoData);
    
    if(SetupDiEnumDeviceInfo(
        hDeviceInfo,            // Our device tree
        nDevice,            // The member to look for
        &devInfoData
        ))
    {
        DWORD regDataType;
    
        BYTE hardwareId[300];
        if(SetupDiGetDeviceRegistryProperty(hDeviceInfo, &devInfoData, SPDRP_HARDWAREID, &regDataType, hardwareId, sizeof(hardwareId), NULL))
        {
    ...
    

    (在循环中使用递增的nDevice调用此位)

    BYTE friendlyName[300];
            if(SetupDiGetDeviceRegistryProperty(hDeviceInfo, &devInfoData, SPDRP_FRIENDLYNAME, NULL, friendlyName, sizeof(friendlyName), NULL))
            {
                strFriendlyNames += (LPCTSTR)friendlyName;
                strFriendlyNames += '\n';
            }
    

    它查找设备的名称。

    希望这能帮助你朝着正确的方向前进。

        2
  •  4
  •   Ilya    15 年前

    确定串行端口设备为所需设备后(通过查看其友好名称、检查其父设备等),获取端口名称的正确方法可能是:

    • 援引 SetupDiOpenDevRegKey(hDevInfo, devInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ) HKEY
    • 查询此注册表项以查找 REG_SZ
    • 别忘了关上窗户 HKEY :)

    然而,这在C语言中可能需要如此多的互操作,这一点都不好笑,所以如果您坚持使用字符串解析解决方案,我不会责怪您。

        3
  •  3
  •   Community paulsm4    7 年前

    发布的信息 Will Dean http://www.pinvoke.net . 我确实需要在这里或那里更改数据类型以使其正常工作(比如使用枚举而不是uint),但这应该很容易理解。

    internal static string GetComPortByDescription(string Description)
    {
        string Result = string.Empty;
        Guid guid = PInvoke.GUID_DEVCLASS_PORTS;
        uint nDevice = 0;
        uint nBytes = 300;
        byte[] retval = new byte[nBytes];
        uint RequiredSize = 0;
        uint PropertyRegDataType = 0;
    
        PInvoke.SP_DEVINFO_DATA devInfoData = new PInvoke.SP_DEVINFO_DATA();
        devInfoData.cbSize = Marshal.SizeOf(typeof(PInvoke.SP_DEVINFO_DATA));
    
        IntPtr hDeviceInfo = PInvoke.SetupDiGetClassDevs(
            ref guid, 
            null, 
            IntPtr.Zero, 
            PInvoke.DIGCF.DIGCF_PRESENT);
    
        while (PInvoke.SetupDiEnumDeviceInfo(hDeviceInfo, nDevice++, ref devInfoData))
        {
            if (PInvoke.SetupDiGetDeviceRegistryProperty(
                    hDeviceInfo, 
                    ref devInfoData, 
                    PInvoke.SPDRP.SPDRP_FRIENDLYNAME,
                    out PropertyRegDataType, 
                    retval, 
                    nBytes, 
                    out RequiredSize))
            {
                if (System.Text.Encoding.Unicode.GetString(retval).Substring(0, Description.Length).ToLower() ==
                    Description.ToLower())
                {
                    string tmpstring = System.Text.Encoding.Unicode.GetString(retval);
                    Result = tmpstring.Substring(tmpstring.IndexOf("COM"),tmpstring.IndexOf(')') - tmpstring.IndexOf("COM"));
                } // if retval == description
            } // if (PInvoke.SetupDiGetDeviceRegistryProperty( ... SPDRP_FRIENDLYNAME ...
        } // while (PInvoke.SetupDiEnumDeviceInfo(hDeviceInfo, nDevice++, ref devInfoData))
    
        PInvoke.SetupDiDestroyDeviceInfoList(hDeviceInfo);
        return Result;
    }
    

    我想这条线 Result = tmpstring.Substring(tmpstring.IndexOf("COM"),tmpstring.IndexOf(')') - tmpstring.IndexOf("COM")); 有点笨手笨脚,建议如何清理它将不胜感激。

    谢谢你在这件事上的帮助,如果没有你,我仍然会搜索谷歌。

        4
  •  2
  •   Leherenn    6 年前

    #include <windows.h>
    #include <initguid.h>
    #include <devguid.h>
    #include <setupapi.h>
    
    void enumerateSerialPortsFriendlyNames()
    {
        SP_DEVINFO_DATA devInfoData = {};
        devInfoData.cbSize = sizeof(devInfoData);
    
        // get the tree containing the info for the ports
        HDEVINFO hDeviceInfo = SetupDiGetClassDevs(&GUID_DEVCLASS_PORTS,
                                                   0,
                                                   nullptr,
                                                   DIGCF_PRESENT
                                                   );
        if (hDeviceInfo == INVALID_HANDLE_VALUE)
        {
            return;
        }
    
        // iterate over all the devices in the tree
        int nDevice = 0;
        while (SetupDiEnumDeviceInfo(hDeviceInfo,            // Our device tree
                                     nDevice++,            // The member to look for
                                     &devInfoData))
        {
            DWORD regDataType;
            DWORD reqSize = 0;
    
            // find the size required to hold the device info
            SetupDiGetDeviceRegistryProperty(hDeviceInfo, &devInfoData, SPDRP_HARDWAREID, nullptr, nullptr, 0, &reqSize);
            BYTE* hardwareId = new BYTE[(reqSize > 1) ? reqSize : 1];
            // now store it in a buffer
            if (SetupDiGetDeviceRegistryProperty(hDeviceInfo, &devInfoData, SPDRP_HARDWAREID, &regDataType, hardwareId, sizeof(hardwareId) * reqSize, nullptr))
            {
                // find the size required to hold the friendly name
                reqSize = 0;
                SetupDiGetDeviceRegistryProperty(hDeviceInfo, &devInfoData, SPDRP_FRIENDLYNAME, nullptr, nullptr, 0, &reqSize);
                BYTE* friendlyName = new BYTE[(reqSize > 1) ? reqSize : 1];
                // now store it in a buffer
                if (!SetupDiGetDeviceRegistryProperty(hDeviceInfo, &devInfoData, SPDRP_FRIENDLYNAME, nullptr, friendlyName, sizeof(friendlyName) * reqSize, nullptr))
                {
                    // device does not have this property set
                    memset(friendlyName, 0, reqSize > 1 ? reqSize : 1);
                }
                // use friendlyName here
                delete[] friendlyName;
            }
            delete[] hardwareId;
        }
    }
    
        5
  •  1
  •   Will Dean    16 年前

    很高兴它起作用了。

    你可以试试:

    Regex.Match(tmpstring,@“COM\s\d+”).ToString()

    用于字符串匹配。

    作为.NET风格的要点,我会添加一个“using System.Text”,并且我不会以大写字母开头局部变量名,如果我真的觉得很有价值,我可能会将setupDiDestroyDeviceInfo列表放在finally{}子句中。

        6
  •  1
  •   JOE    4 年前

    我建立了一个用于串口控制的库。它可以在注册表中搜索友好名称。这里是链接。

    https://github.com/kcwongjoe/serial_port

    std::vector<SerialPortInfo> comPorts = SerialPort::getSerialPortList();
    std::cout << comPorts[0].friendlyName << std::endl;
    
        7
  •  0
  •   Eqbal Khan    9 年前

    使用发布的方法 LiGenChen

        8
  •  0
  •   zezba9000    4 年前

    根据答案的组合,这里有一个解决方案,可以获得COM编号、VID/PID和友好名称等。

    public static class SerialPortUtils
    {
        private static Guid GUID_DEVCLASS_PORTS = new Guid(0x4d36e978u, 0xe325, 0x11ce, 0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18);
    
        private unsafe static bool GetPortRegistryProperty(HDEVINFO classHandle, SP_DEVINFO_DATA* deviceInfo, uint spdrp, out string result)
        {
            DWORD size;
            SetupAPI.SetupDiGetDeviceRegistryPropertyW(classHandle, deviceInfo, spdrp, null, null, 0, &size);
            if (size == 0)
            {
                result = null;
                return false;
            }
    
            var resultBuffer = new byte[(int)size];
            fixed (byte* resultBufferPtr = resultBuffer)
            {
                if (SetupAPI.SetupDiGetDeviceRegistryPropertyW(classHandle, deviceInfo, spdrp, null, resultBufferPtr, size, null))
                {
                    result = Encoding.Unicode.GetString(resultBufferPtr, (int)size - sizeof(char));
                    return true;
                }
                else
                {
                    result = null;
                    return false;
                }
            }
        }
    
        public unsafe static List<SerialPortDeviceDesc> GetSerialPortDevices()
        {
            var results = new List<SerialPortDeviceDesc>();
    
            // get present ports handle
            var classHandle = SetupAPI.SetupDiGetClassDevsW(ref GUID_DEVCLASS_PORTS, null, IntPtr.Zero, SetupAPI.DIGCF_PRESENT);
            if (classHandle == Common.INVALID_HANDLE_VALUE || classHandle == HDEVINFO.Zero) throw new Exception("SetupDiGetClassDevsW failed");
    
            // enumerate all ports
            var deviceInfo = new SP_DEVINFO_DATA();
            uint deviceInfoSize = (uint)Marshal.SizeOf<SP_DEVINFO_DATA>();
            deviceInfo.cbSize = deviceInfoSize;
            uint index = 0;
            while (SetupAPI.SetupDiEnumDeviceInfo(classHandle, index, &deviceInfo))
            {
                // get port name
                string portName;
                HKEY regKey = SetupAPI.SetupDiOpenDevRegKey(classHandle, &deviceInfo, SetupAPI.DICS_FLAG_GLOBAL, 0, SetupAPI.DIREG_DEV, WinNT.KEY_READ);
                if (regKey == Common.INVALID_HANDLE_VALUE || regKey == IntPtr.Zero) continue;
                using (var regHandle = new SafeRegistryHandle(regKey, true))
                using (var key = RegistryKey.FromHandle(regHandle))
                {
                    portName = key.GetValue("PortName") as string;
                    if (string.IsNullOrEmpty(portName)) continue;
                }
    
                // get registry values
                if (!GetPortRegistryProperty(classHandle, &deviceInfo, SetupAPI.SPDRP_FRIENDLYNAME, out string friendlyName)) continue;
                if (!GetPortRegistryProperty(classHandle, &deviceInfo, SetupAPI.SPDRP_HARDWAREID, out string hardwareID)) continue;
    
                // add device
                results.Add(new SerialPortDeviceDesc(friendlyName, portName, hardwareID));
    
                // setup for next device
                ++index;
                deviceInfo = new SP_DEVINFO_DATA();
                deviceInfo.cbSize = deviceInfoSize;
            }
    
            // finish
            SetupAPI.SetupDiDestroyDeviceInfoList(classHandle);
            return results;
        }
    }
    

    下面是SerialPortDeviceDesc类

    public enum SerialPortType
    {
        Unknown,
        COM
    }
    
    public class SerialPortDeviceDesc
    {
        public readonly string friendlyName, portName, hardwareID;
        public readonly string vid, pid;
        public readonly int portNumber = -1;
        public readonly SerialPortType portType = SerialPortType.Unknown;
    
        public SerialPortDeviceDesc(string friendlyName, string portName, string hardwareID)
        {
            this.friendlyName = friendlyName;
            this.portName = portName;
            this.hardwareID = hardwareID;
    
            if (portName.StartsWith("COM") && int.TryParse(portName.Substring("COM".Length), out portNumber))
            {
                portType = SerialPortType.COM;
            }
            else
            {
                portNumber = -1;
            }
    
            var rx = Regex.Match(hardwareID, @"VID_(\w*)&PID_(\w*)", RegexOptions.IgnoreCase);
            if (rx.Success)
            {
                vid = rx.Groups[1].Value;
                pid = rx.Groups[2].Value;
            }
        }
    }