Получение списка доступных 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;
}

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

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

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

  2. Красивый способ всем хорош, да только не на всех системах реестр доступен обычному пользователю. Либо запускать от имени Администратора, либо искать другие решения.

    • На программном уровне реестр доступен всегда. От него зависит очень много приложений (в том числе такие простые как «Пасьянс «Паук» и «Сапер»). И если перекрыть к нему доступ, то пользователь не сможет работать. Администраторы часто запрещают «редактирование реестра», но по факту это просто запрет на использование утилиты regedit. Сам реестр по-прежнему доступен.
      Также обычно пользователю запрещено писать в ветки отличные от HKEY_CURRENT_USER. Но в данном примере мы ничего не пишем, только читаем. А читать можно.

Добавить комментарий для Андрей Отменить ответ

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