
Как получить список COM-портов на Ассемблере

Как получить список COM-портов на Ассемблере
COM-порт (последовательный порт) - это аппаратный интерфейс, предназначенный для последовательной передачи данных. С его помощью к устройству можно подключать внешние периферийные компоненты для обмена информацией или расширения функциональности основного оборудования. В современных системах чаще используется виртуальный COM-порт - программный интерфейс, эмулирующий поведение традиционного последовательного порта. Он позволяет приложениям обмениваться данными между собой или с виртуальными устройствами без необходимости в физическом оборудовании. Независимо от того, физический это порт или виртуальный, с точки зрения программного взаимодействия они выглядят одинаково. Поэтому в этой статье мы узнаем, как получить список доступных COM-портов с использованием Ассемблера.
Самый простой способ получить список всех COM-портов - обратиться к ветке реестра HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM. В ней каждая запись содержит имя устройства (в качестве имени параметра) и соответствующее ему обозначение порта, например "COM3". Достаточно последовательно прочитать все значения из этого раздела, чтобы получить полный перечень доступных последовательных портов. Такой подход работает как для физических, так и для виртуальных COM-портов и не требует установки дополнительных драйверов или библиотек.
Code (Assembler) : Убрать нумерацию
- ; Открываем ключ HKLM\HARDWARE\DEVICEMAP\SERIALCOMM
- invoke RegOpenKeyEx,HKEY_LOCAL_MACHINE,\
- subkey,0,KEY_READ,hKey
- cmp eax,ERROR_SUCCESS
- jne loc_error
- ; Начать перебор с первого устройства
- mov [index],0
- loc_loop:
- ; Очистить строки
- invoke RtlZeroMemory,devname,LEN_STRING
- invoke RtlZeroMemory,portname,LEN_STRING
- ; Нужно сбрасывать размеры буферов
- mov [devsize],LEN_STRING
- mov [portsize],LEN_STRING
- ; Читаем значение по индексу
- invoke RegEnumValue,[hKey],[index],\
- devname,devsize,\
- 0,tmp,\
- portname,portsize
- ; Портов больше нет
- cmp eax,ERROR_NO_MORE_ITEMS
- je loc_done
- ; Проверяем на другие ошибки
- cmp eax,ERROR_SUCCESS
- jne loc_done
- ; devname -> драйвер, например, "Serial0" или типа такого
- ; portname -> порт, например, "COM3" или "COM10"
- ...
- ; Увеличиваем индекс
- inc [index]
- jmp loc_loop
- loc_done:
- ; Закрываем ключ
- invoke RegCloseKey,[hKey]
- ; Done
- ...
- loc_error:
- ; ERROR
- ...
Code (Assembler) : Убрать нумерацию
- ; Структура SetupAPI
- struct SP_DEVINFO_DATA
- cbSize dd ? ; Размер структуры в байтах
- ClassGuid rb 16 ; GUID класса устройства
- DevInst dd ? ; Дескриптор экземпляра устройства
- Reserved dd ? ; Зарезервировано
- ends
- ; Константы SetupAPI
- DIGCF_PRESENT = 0x00000002
- DIGCF_PROFILE = 0x00000008
- SPDRP_HARDWAREID = 0x00000001
- SPDRP_FRIENDLYNAME = 0x0000000C
- ERROR_ACCESS_DENIED = 5
- ; GUID {4D36E978-E325-11CE-BFC1-08002BE10318}
- GUID_DEVCLASS_PORTS \
- dd 04D36E978h
- dw 0E325h
- dw 011CEh
- db 0BFh, 0C1h, 008h, 000h, 02Bh, 0E1h, 003h, 018h
Нас интересует свойство SPDRP_FRIENDLYNAME, то есть "человеческое" имя устройства, отображаемое в системе. Следует помнить, что SetupAPI является универсальным интерфейсом и работает со всеми типами устройств, а не только с COM-портами. Поэтому в SPDRP_FRIENDLYNAME будет содержаться обобщенное описание, например: "USB Serial Device (COM3)" или "Prolific USB-to-Serial Comm Port (COM12)". Чтобы извлечь именно имя COM-порта, потребуется дополнительно распарсить эту строку и выделить подстроку вида "COM3" или "COM12". При работе с COM-портами в коде важно учитывать следующее: если имя порта COM1-COM9, его можно указывать в виде простой строки, например "COM3". Однако начиная с COM10 и выше стандартное обозначение уже не работает напрямую. В таких случаях необходимо использовать расширенный синтаксис Windows, например "\\.\COM11". Этот формат сообщает системе, что вы обращаетесь к устройству через диспетчер устройств Windows, что позволяет корректно работать с портами, имеющими номер 10 и выше. Поэтому рекомендуется использовать единообразный формат \\.\COMxx для всех COM-портов, независимо от их номера. Это гарантирует совместимость, избавляет от необходимости проверять номер порта и предотвращает ошибки при работе с портами начиная с COM10 и выше.
Чтобы найти именно интересующее вас устройство, следует получить его Hardware ID, запросив свойство по индексу SPDRP_HARDWAREID. Затем можно отфильтровать весь список устройств, сравнивая Hardware ID с нужным значением. Такой подход позволяет однозначно идентифицировать требуемое устройство и получить соответствующее ему имя COM-порта, обеспечивая точное сопоставление между портом и физическим (или виртуальным) устройством.
Последним шагом станет проверка доступности порта: для этого нужно попытаться открыть системный путь вида \\.\COMx с помощью функции CreateFile. Результат вызова покажет, существует ли порт, свободен ли он, недоступен или уже используется другим приложением. Это позволяет убедиться, что выбранный COM-порт действительно можно использовать.
Теперь можно переходить к программированию. Собираем все вместе и посмотрим, что у нас получается.
Code (Assembler) : Убрать нумерацию
- ; Получаем список устройств класса Ports
- invoke SetupDiGetClassDevsA,GUID_DEVCLASS_PORTS,\
- 0,0,DIGCF_PRESENT or DIGCF_PROFILE
- cmp eax,-1
- je loc_error
- mov [hDevInfo],eax
- mov [SPDID.cbSize],sizeof.SP_DEVINFO_DATA
- ; Начать перебор с первого устройства
- mov [index],0
- loc_loop:
- ; Перебираем устройства одно за другим
- invoke SetupDiEnumDeviceInfo,[hDevInfo],[index],SPDID
- test eax,eax
- jz loc_done
- ; Получаем Hardware ID
- mov byte [szHWID],0
- invoke SetupDiGetDeviceRegistryPropertyA,[hDevInfo],SPDID,\
- SPDRP_HARDWAREID,tmp,szHWID,LEN_BUFFER,NULL
- ; Может быть HardwareID пустая строка
- cmp byte [szHWID],0
- jne @f
- ; Unknown
- invoke lstrcpy,szHWID,szUnknown
- @@:
- ; Получаем Friendly Name
- mov byte [szFriendly],0
- invoke SetupDiGetDeviceRegistryPropertyA,[hDevInfo],SPDID,\
- SPDRP_FRIENDLYNAME,tmp,szFriendly,LEN_BUFFER,NULL
- ; Может быть FriendlyName пустая строка
- cmp byte [szFriendly],0
- jne @f
- ; Если имени нет,пропускаем
- invoke lstrcpy,szFriendly,szUnknown
- invoke lstrcpy,szFull,szUnknown
- mov ebx,szUnknown
- jmp loc_log
- @@:
- ; Парсинг "COMx" из строки
- mov esi,szFriendly
- loc_com_parse:
- lodsb
- or al,al
- jnz @f
- ; Конец строки, COM не найден
- invoke lstrcpy,szFull,szUnknown
- mov ebx,szUnknown
- jmp loc_log
- @@:
- dec esi
- ; Ищем сигнатуру "(COM"
- cmp dword [esi],'(COM'
- jne skip_char
- ; Нашли, ESI указывает на '('
- inc esi
- ; Сдвигаем на начало имени порта
- mov edi,szPort
- loc_copy:
- ; Копируем до закрывающей скобки
- lodsb
- cmp al,')'
- je loc_done_parse
- or al,al
- jz loc_done_parse
- stosb
- jmp loc_copy
- skip_char:
- inc esi
- jmp loc_com_parse
- loc_done_parse:
- ; Null-terminator
- mov al,0
- stosb
- ; Формируем системный путь \\.\COMx
- invoke wsprintf,szFull,mask_com,szPort
- add esp,12
- ; Проверяем доступность порта
- invoke CreateFile,szFull,GENERIC_READ or GENERIC_WRITE,\
- 0,NULL,OPEN_EXISTING,0,NULL
- mov ebx,eax
- cmp eax,INVALID_HANDLE_VALUE
- je loc_check
- ; Порт свободен
- mov ebx,szFree
- invoke CloseHandle,ebx
- jmp loc_log
- loc_check:
- invoke GetLastError
- cmp eax,ERROR_ACCESS_DENIED
- je loc_busy
- ; Ошибка (не занят, а что-то другое)
- invoke wsprintf,szStatus,mask_er,eax
- add esp,12
- mov ebx,szStatus
- jmp loc_log
- loc_busy:
- mov ebx,szBusy
- loc_log:
- ; Вывод в лог
- ; szHWID -> данные Hardware ID
- ; szFriendly -> название устройства
- ; szFull -> полное название порта
- ...
- loc_next:
- ; Увеличиваем индекс
- inc [index]
- jmp loc_loop
- loc_done:
- invoke SetupDiDestroyDeviceInfoList,[hDevInfo]
- ; Done
- ...
- loc_error:
- ; Error
- ...
Просмотров: 357 | Комментариев: 8
Метки: Assembler
Комментарии
Отзывы посетителей сайта о статье
ManHunter
(17.05.2026 в 20:25):
Да, именно так и должно быть. Это неведомая херня, которая никогда не будет правильно определяться и, соответственно, не будет работать как COM-порт. Большое спасибо за тестирование.
Maklen
(16.05.2026 в 22:35):
'''get_com.exe log
Device: \Device\Serial0
Port: COM1
-------------
Device: Winachsf0
Port: COM3
-------------
Done
'''get_info.exe log
Hardware ID: ACPI\VEN_PNP&DEV_0400
Friendly Name: Порт принтера (LPT1)
Port: Unknown
Status: Unknown
-------------
Hardware ID: ACPI\VEN_PNP&DEV_0501
Friendly Name: ?Последовательный порт (COM1)
Port: \\.\COM1
Status: Free
-------------
Done
ManHunter
(15.05.2026 в 20:52):
Доработал. Будет показываться по крайней мере список устройств. Для странных устройств будет выдаваться "Unknown". Исходники и архив обновлен.
Maklen
(15.05.2026 в 10:53):
Это старый https://www.dlink.ru/ru/products/11/186.html валялся под рукой. По TAPI выдал длинный лог.
ManHunter
(15.05.2026 в 10:24):
Maklen, спасибо, теперь есть понимание. Осталось найти такой девайс :)
Проблема в том, что система возвращает поле FriendlyName как пустая строка, или оно есть в списке устройств как "Какой-то неведомой порт" без в строки "(COM3)" в названии. В тестовой программе get_info пропускает такие устройства. Я подумаю, что можно сделать.
Проблема в том, что система возвращает поле FriendlyName как пустая строка, или оно есть в списке устройств как "Какой-то неведомой порт" без в строки "(COM3)" в названии. В тестовой программе get_info пропускает такие устройства. Я подумаю, что можно сделать.
Maklen
(15.05.2026 в 08:19):
Win10x64
'''get_com.exe log
Get COM-port List
Device: \Device\Serial0
Port: COM1
-------------
Device: Winachsf0
Port: COM3
-------------
Done
'''get_info.exe log
Get COM-port List
Hardware ID: ACPI\VEN_PNP&DEV_0501
Friendly Name: Последовательный порт (COM1)
Port: \\.\COM1
Status: Free
-------------
Done
'''get_com.exe log
Get COM-port List
Device: \Device\Serial0
Port: COM1
-------------
Device: Winachsf0
Port: COM3
-------------
Done
'''get_info.exe log
Get COM-port List
Hardware ID: ACPI\VEN_PNP&DEV_0501
Friendly Name: Последовательный порт (COM1)
Port: \\.\COM1
Status: Free
-------------
Done
ManHunter
(13.05.2026 в 13:47):
Слишком мало данных. Я так же могу сказать, что все прекрасно отображает и видит. Тестировал на Win7x86, Win7x64 и Win10x64, все работает. Пруф: https://ibb.co/5X549X3W
Maklen
(12.05.2026 в 09:29):
get_info не отображает USB СОМ порт, get_com его видит..
Добавить комментарий
Заполните форму для добавления комментария
Примеры программ с исходными текстами (FASM)

