Как получить понятное имя COM-порта в Windows?
-
08-07-2019 - |
Вопрос
У меня есть GSM-модем, подключенный через USB.Модем создает 2 последовательных порта.Первый автоматически подключается к модему, второй отображается в диспетчере устройств как «HUAWEI Mobile Connect — 3G PC UI Interface (COM6)».
Второй порт используется для получения важной информации от модема, например, о качестве сигнала;отправлять и получать текстовые сообщения;и целый ряд других функций.
Я пишу приложение, которое реализует некоторые функции второго порта.Что мне нужно, так это надежный метод определения того, какой COM-порт является запасным.Перебора портов и проверки ответа на «ATE0» недостаточно.Порт модема обычно имеет меньший номер, и когда коммутируемое соединение не активно, он будет реагировать на «ATE0» так же, как и второй порт.
Я думал о том, чтобы перебрать порты и проверить их понятное имя, как оно отображается в диспетчере устройств.Таким образом, я могу связать порт в своем приложении с портом с надписью «HUAWEI Mobile Connect — 3G PC UI Interface (COM6)» в диспетчере устройств.Я просто еще не нашел никакой информации, которая позволила бы мне получить это имя программно.
Решение
Давным-давно я написал утилиту для клиента, которая делает именно это, но для 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, ®DataType, hardwareId, sizeof(hardwareId), NULL))
{
...
(этот бит вызывается в цикле с увеличением nDevice)
а затем
BYTE friendlyName[300];
if(SetupDiGetDeviceRegistryProperty(hDeviceInfo, &devInfoData, SPDRP_FRIENDLYNAME, NULL, friendlyName, sizeof(friendlyName), NULL))
{
strFriendlyNames += (LPCTSTR)friendlyName;
strFriendlyNames += '\n';
}
который находит имя устройства.
Надеюсь, это поможет вам в правильном направлении.
Другие советы
После того, как вы определите, что устройство с последовательным портом является тем, которое вам нужно (посмотрев на его понятное имя, проверив его родительское устройство и т. д.), правильный способ получить имя порта, вероятно, будет следующим:
- вызывать
SetupDiOpenDevRegKey(hDevInfo, devInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ)
чтобы получитьHKEY
к так называемому ключу устройства - запросите этот ключ реестра для
REG_SZ
значение «ИмяПорта» - не забудь закрыть
HKEY
:)
Однако для этого может потребоваться столько взаимодействия с C#, что это даже не смешно, поэтому я не виню вас, если вы придерживаетесь решения по синтаксическому анализу строк.
Информация, размещенная Уиллом Дином , оказалась наиболее полезной. Это код, который в итоге сработал для меня. Все в классе 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.
Версия 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, ®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;
}
}
Рад, что это сработало. Р>
Вы можете попробовать:
Regex.Match (tmpstring, @ " COM \ s \ d + "). ToString ()
для вашего соответствия строк.
В качестве точек стиля .NET я бы добавил " с использованием System.Text " и не стал бы начинать имена локальных переменных с заглавных букв, и если бы я чувствовал себя действительно добродетельным, я бы, вероятно, поместил бы SetupDiDestroyDeviceInfoList в предложение finally {}.
Использовал метод, опубликованный LiGenChen а>. Метод ComPortSetupAPISetupDiClassGuids дал лучшее время и понятное имя.