Come posso ottenere il nome descrittivo di una porta COM in Windows?
-
08-07-2019 - |
Domanda
Ho un modem GSM collegato tramite USB. Il modem crea 2 porte seriali. Il primo viene automaticamente collegato al modem, il secondo viene visualizzato in Gestione dispositivi come "HUAWEI Mobile Connect - Interfaccia UI PC 3G (COM6)"
La seconda porta viene utilizzata per ottenere informazioni vitali dal modem, come la qualità del segnale; per inviare e ricevere messaggi di testo; e tutta una serie di altre funzioni.
Sto scrivendo un'applicazione che racchiuderà alcune delle funzionalità fornite dalla seconda porta. Quello di cui ho bisogno è un metodo sicuro per identificare quale porta COM è quella di riserva. Iterazione delle porte e controllo di una risposta a "ATE0" non è sufficiente. La porta del modem è in genere quella con il numero più basso e quando una connessione remota non è attiva, risponderà a "ATE0". lo stesso della seconda porta.
Quello che stavo pensando di fare è iterare le porte e controllare il loro nome descrittivo, come mostra Device Manager. In questo modo posso collegare la porta della mia applicazione alla porta etichettata "HUAWEI Mobile Connect - Interfaccia UI PC 3G (COM6)" in Gestione dispositivi. Non ho ancora trovato alcuna informazione che mi permetta di ottenere quel nome a livello di codice.
Soluzione
Molto tempo fa ho scritto un'utilità per un client per fare proprio questo, ma per un GPS piuttosto che un modem.
L'ho appena visto, e i bit che saltano fuori come forse utili sono:
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, ®DataType, hardwareId, sizeof(hardwareId), NULL))
{
...
(questo bit viene chiamato in un ciclo con incremento di nDevice)
e poi
BYTE friendlyName[300];
if(SetupDiGetDeviceRegistryProperty(hDeviceInfo, &devInfoData, SPDRP_FRIENDLYNAME, NULL, friendlyName, sizeof(friendlyName), NULL))
{
strFriendlyNames += (LPCTSTR)friendlyName;
strFriendlyNames += '\n';
}
che trova il nome del dispositivo.
Speriamo che ti possa aiutare nella giusta direzione.
Altri suggerimenti
Dopo aver determinato un dispositivo con porta seriale è quello desiderato (guardando il suo nome descrittivo, controllando il suo dispositivo padre ecc.), il modo corretto per ottenere il nome della porta sarebbe probabilmente:
- invoca
SetupDiOpenDevRegKey (hDevInfo, devInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ)
per ottenereHKEY
sulla cosiddetta chiave del dispositivo - interroga questa chiave di registro per il
REG_SZ
valore " PortName " - non dimenticare di chiudere
HKEY
:)
Tuttavia, questo potrebbe richiedere così tanto interoperabilità in C #, non è nemmeno divertente, quindi non ti biasimo se rimani sulla soluzione di analisi delle stringhe.
Le informazioni pubblicate da Will Dean sono state di grande aiuto. Questo è il codice che alla fine ha funzionato per me. Tutto nella classe PInvoke è stato preso alla lettera da http://www.pinvoke.net . Ho dovuto cambiare un tipo di dati qui o là per farlo funzionare (come quando si utilizza un enum anziché un uint) ma dovrebbe essere facile da capire.
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;
}
Penso che la riga Risultato = tmpstring.Substring (tmpstring.IndexOf (" COM "), tmpstring.IndexOf (')') - tmpstring.IndexOf (" COM "));
è un po 'goffo, i suggerimenti su come ripulirlo sarebbero apprezzati.
Grazie per il tuo aiuto in questa faccenda Will, senza di te, starei ancora cercando su Google.
La versione C ++ basata sulla risposta @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, ®DataType, 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;
}
}
Sono contento che abbia funzionato.
Potresti provare:
Regex.Match (tmpstring, @ " COM \ s \ d + "). ToString ()
per la corrispondenza delle stringhe.
Come indica lo stile .NET, aggiungerei un " usando System.Text " ;, e non inizierei i nomi delle variabili locali con le maiuscole e, se mi sentissi veramente virtuoso, probabilmente inserirei SetupDiDestroyDeviceInfoList in un infine la clausola {}.
Utilizzato il metodo pubblicato da LiGenChen . Il metodo ComPortSetupAPISetupDiClassGuids ha dato il miglior tempo e il nome descrittivo.