Получение списка доступных COM портов

При программировании COM портов полезно иметь возможность получать список доступных портов на компьютере. Эта задача встречается настолько часто, что я решил затронуть ее в своем блоге.

Существует несколько различных способов решения этой задачи. Их все можно условно разделить на «некрасивые» и «красивые».

«Некрасивые» решения

Первый вариант заключается в том, чтобы зашить в программу самые распространенные номера портов (обычно COM1, COM2, COM3 и некоторые другие). Недостатком этого подхода является то, что вы не всегда можете охватить все доступные порты. Например, я видел системы, на которых встречается порты COM22, COM23, COM24 (порты были виртуальными, но суть вопроса это не меняет). Не будете же вы зашивать столько номеров. А если завтра встретится COM100?

Второй вариант заключается в проверке большого количества портов на их доступность. Мы просматриваем порты COM1, COM2, ….., COM100 и пытаемся открыть каждый из них. Если это удалось, значит, порт есть. У этого подхода несколько недостатков.

Во-первых, такой перебор занимает время, а регулярные вызовы функции CreateFile напрасно расходуют системные ресурсы.

Во-вторых, у данного метода возможны ложные срабатывания. Предположим, пользователь запустил программу, которая заняла порт COM3. После этого он запустил вашу программу. Разумеется, при попытке открыть порт COM3 возникнет ошибка, так как он занят другой программой. В результате чего программа решит, что такого порта нет. А он есть.

В-третьих, он не решает проблему охвата всего многообразия возможных портов.

«Красивое» решение

Красивое решение основывается на том факте, что информация о доступных COM портах (в том числе виртуальных) в системах Windows хранится в реестре. Точнее в ветке HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM\. Там COM порты хранятся в виде строк: «COM1», «COM2», «COM3» и т.д. Отсюда сразу возникает решение – нужно перебрать все строковые параметры в данном разделе реестра.

Ниже приводится полный исходный код примера, демонстрирующего данный метод.

#include <windows.h>
#include <tchar.h>

void ShowCOMPorts()
{
  int r = 0;
  HKEY hkey = NULL;
  //Открываем раздел реестра, в котором хранится иинформация о COM портах
  r = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("HARDWARE\DEVICEMAP\SERIALCOMM\"), 0, KEY_READ, &hkey);
  if (r != ERROR_SUCCESS) 
    return;
  
  unsigned long CountValues = 0, MaxValueNameLen = 0, MaxValueLen = 0;
  //Получаем информацию об открытом разделе реестра
  RegQueryInfoKey(hkey, NULL, NULL, NULL, NULL, NULL, NULL, &CountValues, &MaxValueNameLen, &MaxValueLen, NULL, NULL);
  ++MaxValueNameLen;
  //Выделяем память
  TCHAR *bufferName = NULL, *bufferData = NULL;
  bufferName = (TCHAR*)malloc(MaxValueNameLen * sizeof(TCHAR));
  if (!bufferName)
  {
    RegCloseKey(hkey);
    return;
  }
  bufferData = (TCHAR*)malloc((MaxValueLen + 1)*sizeof(TCHAR));
  if (!bufferData) 
  { 
    free(bufferName); 
    RegCloseKey(hkey);
    return;
  }
  
  unsigned long NameLen, type, DataLen;
  //Цикл перебора параметров раздела реестра
  for (unsigned int i = 0; i < CountValues; i++)
  {
    NameLen = MaxValueNameLen;
    DataLen = MaxValueLen;
    r = RegEnumValue(hkey, i, bufferName, &NameLen, NULL, &type, (LPBYTE)bufferData, &DataLen);
    if ((r != ERROR_SUCCESS) || (type != REG_SZ))
      continue;		
    
    _tprintf(TEXT("%s\n"), bufferData);
  }
  //Освобождаем память
  free(bufferName);
  free(bufferData);
  //Закрываем раздел реестра
  RegCloseKey(hkey);
}

int main()
{
  ShowCOMPorts();
  return 0;
}

В заключение лишь отмечу, что я использую этот подход во многих своих проектах, и он еще ни разу не подводил меня.

2 replies on “Получение списка доступных COM портов”

  1. Сутченков Игорь:

    Спасибо за пример программы, программа работает.

  2. евгений:

    да, рабочий пример, но в цикле я поправил по возрастающей
    for (unsigned int i = 0; i 0; i—) — замена

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *