Как получить понятное имя COM-порта в Windows? - PullRequest
14 голосов
/ 20 ноября 2008

У меня GSM модем, подключенный через USB. Модем создает 2 последовательных порта. Первый автоматически подключается к модему, второй отображается в диспетчере устройств как «HUAWEI Mobile Connect - 3G PC UI Interface (COM6)» *

Второй порт используется для получения важной информации от модема, такой как качество сигнала; отправлять и получать текстовые сообщения; и целый ряд других функций.

Я пишу приложение, которое обернет некоторые функции, предоставляемые вторым портом. Что мне нужно, так это надежный метод определения того, какой COM-порт является запасным. Итерации портов и проверки ответа на «ATE0» недостаточно. Порт модема обычно имеет номер с меньшим номером, и когда коммутируемое соединение не активно, оно ответит на «ATE0» так же, как и второй порт.

Я думал об итерации портов и проверке их понятного имени, как показано в диспетчере устройств. Таким образом, я могу связать порт в моем приложении с портом, помеченным как «HUAWEI Mobile Connect - 3G PC UI Interface (COM6)» в диспетчере устройств. Я пока не нашел никакой информации, которая позволила бы мне получить это имя программным путем.

Ответы [ 6 ]

8 голосов
/ 20 ноября 2008

Давным-давно я написал утилиту для клиента, чтобы сделать именно это, но для 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';
        }

, который находит имя устройства.

Надеюсь, это поможет вам в правильном направлении.

3 голосов
/ 28 июня 2009

После того, как вы определите, что устройство с последовательным портом является тем, которое вам нужно (просмотрев его Friendly Name, проверив его родительское устройство и т. Д.), Правильный способ получить имя порта, вероятно, будет:

  • вызовите SetupDiOpenDevRegKey(hDevInfo, devInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ), чтобы получить HKEY для так называемого ключа устройства
  • запросить этот раздел реестра для значения REG_SZ "PortName"
  • не забудьте закрыть HKEY:)

Однако, это может потребовать такого большого количества взаимодействия в C #, что это даже не смешно, поэтому я не виню вас, если вы будете использовать решение для анализа строк.

2 голосов
/ 10 августа 2017

Версия C ++, основанная на ответе @Will Dean.

#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;
    }
}
2 голосов
/ 20 ноября 2008

Информация, опубликованная Уиллом Дином , оказалась наиболее полезной. Это код, который в итоге сработал для меня. Все в классе PInvoke было взято дословно из http://www.pinvoke.net. Мне пришлось изменить тип данных здесь или там, чтобы он работал (например, при использовании enum вместо 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")); немного неуклюжа, предложения по ее очистке были бы оценены.

Спасибо за вашу помощь в этом вопросе. Уилл, без вас я бы по-прежнему искал в Google.

1 голос
/ 20 ноября 2008

Рад, что это сработало.

Вы можете попробовать:

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

для вашего соответствия строк.

В качестве точек стиля .NET я бы добавил "using System.Text" и не начинал бы имена локальных переменных с заглавных букв, и если бы я чувствовал себя действительно добродетельным, я, вероятно, поместил бы SetupDiDestroyDeviceInfoList в наконец {} пункт.

0 голосов
/ 20 февраля 2016

Использовал метод, опубликованный LiGenChen . Метод ComPortSetupAPISetupDiClassGuids дал лучшее время и понятное имя.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...