Comment obtenir le nom convivial d'un port COM sous Windows?
-
08-07-2019 - |
Question
J'ai un modem GSM connecté via USB. Le modem crée 2 ports série. Le premier est automatiquement connecté au modem, le second apparaît dans le Gestionnaire de périphériques comme suit: "HUAWEI Mobile Connect - Interface d'interface utilisateur 3G pour PC (COM6)"
Le deuxième port est utilisé pour obtenir des informations vitales du modem, telles que la qualité du signal; envoyer et recevoir des messages texte; et une foule d'autres fonctions.
Je suis en train d’écrire une application qui résumera certaines des fonctionnalités fournies par le deuxième port. Ce dont j'ai besoin, c'est d'une méthode infaillible d'identification du port COM disponible. Itérer les ports et vérifier une réponse à " ATE0 " c'est insuffisant. Le port du modem est généralement celui dont le numéro est le plus petit et, lorsqu'une connexion par numérotation n'est pas active, il répondra à "ATE0". identique au deuxième port.
Ce que je pensais faire est de réitérer les ports et de vérifier leur nom convivial, comme indiqué dans le Gestionnaire de périphériques. Ainsi, je peux relier le port de mon application au port intitulé "HUAWEI Mobile Connect - Interface d'interface utilisateur 3G (COM6)". dans le Gestionnaire de périphériques. Je n'ai tout simplement pas trouvé d'informations qui me permettraient d'obtenir ce nom par programmation.
La solution
Il y a longtemps, j'ai écrit un utilitaire destiné à un client, mais uniquement à un GPS plutôt qu'à un modem.
Je viens de le regarder, et les éléments qui jaillissent comme pouvant être utiles sont:
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))
{
...
(Vous appelez ce bit dans une boucle avec nDevice incrémenté)
et ensuite
BYTE friendlyName[300];
if(SetupDiGetDeviceRegistryProperty(hDeviceInfo, &devInfoData, SPDRP_FRIENDLYNAME, NULL, friendlyName, sizeof(friendlyName), NULL))
{
strFriendlyNames += (LPCTSTR)friendlyName;
strFriendlyNames += '\n';
}
qui trouve le nom du périphérique.
J'espère que cela vous aidera dans la bonne direction.
Autres conseils
Une fois que vous avez déterminé qu'un périphérique de port série est celui que vous souhaitez (en regardant son nom convivial, en vérifiant son périphérique parent, etc.), la méthode appropriée pour obtenir le nom du port serait probablement:
- invoque
SetupDiOpenDevRegKey (hDevInfo, devInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ)
pour obtenir leHKEY
vers la clé de périphérique appelée - interrogez cette clé de registre pour la valeur
REG_SZ
"Nom du port" et - n'oubliez pas de fermer
HKEY
:)
Cependant, cela pourrait nécessiter beaucoup d'interopérabilité en C #, ce n'est même pas drôle, alors je ne vous en veux pas si vous vous en tenez à la solution d'analyse syntaxique des chaînes.
Les informations publiées par Will Dean ont été des plus utiles. C'est le code qui a finalement fonctionné pour moi. Tout dans la classe PInvoke a été repris intégralement de http://www.pinvoke.net . Je devais changer un type de données ici ou là pour que cela fonctionne (comme si vous utilisiez un enum au lieu d'un uint), mais cela devrait être facile à comprendre.
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;
}
Je pense que la ligne Result = tmpstring.Substring (tmpstring.IndexOf ("COM"), tmpstring.IndexOf (')') - tmpstring.IndexOf ("COM"), tmpstring.IndexOf (')' - tmpstring.IndexOf ("COM"), tmpstring.IndexOf (')'); est un peu maladroit, des suggestions sur la façon de le nettoyer seraient appréciées.
Merci de votre aide dans cette affaire Will, sans vous, je chercherais toujours sur Google.
La version C ++ basée sur la réponse @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;
}
}
Heureux cela a fonctionné.
Vous pouvez essayer:
Regex.Match (tmpstring, @ "COM \ s \ d +"). ToString ()
pour votre correspondance de chaîne.
En tant que points de style .NET, j’ajouterais un "using System.Text", et je ne commencerais pas les noms de variables locales par des majuscules, et si je me sentais vraiment vertueux, je mettrais probablement SetupDiDestroyDeviceInfoList dans un finally {} clause.
Utiliser la méthode publiée par LiGenChen . La méthode ComPortSetupAPISetupDiClassGuids a donné le meilleur temps et un nom convivial.