16

我有一个通过 USB 连接的 GSM 调制解调器。调制解调器创建 2 个串行端口。第一个自动连接到调制解调器,第二个在设备管理器中显示为“HUAWEI Mobile Connect - 3G PC UI Interface (COM6)”

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

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

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

4

8 回答 8

8

很久以前,我为客户端编写了一个实用程序来执行此操作,但用于 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';
        }

找到设备的名称。

希望这将帮助您朝着正确的方向前进。

于 2008-11-20T11:31:38.037 回答
4

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

  • 调用SetupDiOpenDevRegKey(hDevInfo, devInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ)以获取HKEY所谓的设备密钥
  • 在此注册表项中查询REG_SZ值“PortName”
  • 不要忘记关闭HKEY:)

但是,这可能需要在 C# 中进行如此多的互操作,这甚至都不好笑,所以如果您坚持使用字符串解析解决方案,我不会责怪您。

于 2009-06-28T15:12:58.593 回答
3

Will Dean发布的信息最有帮助。这是最终对我有用的代码。PInvoke 类中的所有内容均来自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"));有点笨拙,关于如何清理它的建议将不胜感激。

感谢您对此事的帮助 Will,如果没有您,我仍然会在 Google 上搜索。

于 2008-11-20T15:11:24.240 回答
2

基于@Will Dean 答案的C++ 版本。

#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;
    }
}
于 2017-08-10T12:33:34.487 回答
1

很高兴它奏效了。

你可以试试:

正则表达式.Match(tmpstring, @"COM\s\d+").ToString()

为您的字符串匹配。

作为 .NET 风格点,我会添加一个“使用 System.Text”,并且我不会以大写字母开头的局部变量名称,如果我觉得自己很善良,我可能会将 SetupDiDestroyDeviceInfoList 放在 finally{} 子句中.

于 2008-11-20T18:56:09.607 回答
1

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

https://github.com/kcwongjoe/serial_port

std::vector<SerialPortInfo> comPorts = SerialPort::getSerialPortList();
std::cout << comPorts[0].friendlyName << std::endl;
于 2020-11-24T06:05:51.777 回答
0

使用LiGenChen发布的方法。ComPortSetupAPISetupDiClassGuids 方法给出了最好的时间和友好的名称。

于 2016-02-20T02:37:51.413 回答
0

基于此处的答案组合,这是一个获取 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;
        }
    }
}
于 2020-08-07T02:44:16.993 回答