Pregunta

Tengo un módem GSM conectado a través de USB. El módem crea 2 puertos seriales. El primero se conecta automáticamente al módem, el segundo se muestra en el Administrador de dispositivos como "HUAWEI Mobile Connect - Interfaz de interfaz de usuario de PC 3G (COM6)"

El segundo puerto se utiliza para obtener información vital del módem, como la calidad de la señal; para enviar y recibir mensajes de texto; y una gran cantidad de otras funciones.

Estoy escribiendo una aplicación que incluirá algunas de las características proporcionadas por el segundo puerto. Lo que necesito es un método seguro para identificar qué puerto COM es el de reserva. Iterando los puertos y comprobando una respuesta a " ATE0 " No es suficiente. El puerto del módem suele ser el número más bajo, y cuando una conexión de acceso telefónico no está activa, responderá a "ATE0". lo mismo que el segundo puerto.

Lo que estaba pensando hacer es iterar los puertos y verificar su nombre descriptivo, como se muestra en el Administrador de dispositivos. De esa forma, puedo vincular el puerto de mi aplicación al puerto etiquetado como "HUAWEI Mobile Connect - Interfaz de interfaz de usuario de PC 3G (COM6)". en el Administrador de dispositivos. Todavía no he encontrado ninguna información que me permita obtener ese nombre mediante programación.

¿Fue útil?

Solución

Hace mucho tiempo escribí una utilidad para que un cliente hiciera exactamente esto, pero para un GPS en lugar de un módem.

Lo acabo de mirar, y los bits que pueden ser útiles son:

    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))
    {
...

(Llama a este bit en un bucle con nDevice creciente)

y luego

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

que encuentra el nombre del dispositivo.

Espero que eso te ayude en la dirección correcta.

Otros consejos

Después de determinar que un dispositivo de puerto serie es el que desea (mirando su nombre descriptivo, comprobando su dispositivo principal, etc.), la forma correcta de obtener el nombre del puerto probablemente sería:

  • invocar SetupDiOpenDevRegKey (hDevInfo, devInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ) para obtener el HKEY en la llamada clave del dispositivo
  • consulta esta clave de registro para el valor REG_SZ " PortName "
  • no olvide cerrar el HKEY :)

Sin embargo, esto puede requerir tanta interoperabilidad en C # que ni siquiera es divertido, por lo que no te culpo si sigues con la solución de análisis de cadenas.

La información publicada por Will Dean fue de gran ayuda. Este es el código que finalmente funcionó para mí. Todo en la clase PInvoke se tomó textualmente de http://www.pinvoke.net . Tuve que cambiar un tipo de datos aquí o allá para que funcione (como cuando se usa una enumeración en lugar de un uint), pero debería ser fácil de entender.

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

Creo que la línea Result = tmpstring.Substring (tmpstring.IndexOf (" COM "), tmpstring.IndexOf (')') - tmpstring.IndexOf (" COM ")); es un poco torpe, agradeceríamos sugerencias sobre cómo limpiarlo.

Gracias por tu ayuda con este asunto Will, sin ti, todavía estaría buscando en Google.

La versión C ++ basada en la respuesta de @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;
    }
}

Me alegro de que haya funcionado.

Puedes probar:

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

para su coincidencia de cadenas.

Como puntos de estilo .NET, agregaría un " usando System.Text " ;, y no comenzaría los nombres de variables locales con mayúsculas, y si me sintiera realmente virtuoso, probablemente pondría el SetupDiDestroyDeviceInfoList en una finalmente {} cláusula.

Utilizó el método publicado por LiGenChen . El método ComPortSetupAPISetupDiClassGuids dio el mejor momento y el nombre descriptivo.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top