Jump to content
    

Обнаружение пропадания COM-порта из системы.

Вопрос касается виртуальных COM-портов разного рода: как USB CDC, так и разного рода виртуальных COM-портов образованных различными драйверами (bluetooth, виртуальных портов образованных из TCP-сокетов и т.п.).

Дано: Приложение (WinXP...Win11) открыло COM-порт и держит его открытым. Обмен данными по порту предположим - отсутствует. Или приложение ожидает входящих данных, не начиная передачу в порт по своей инициативе. Порт открыт и используется в overlapped-режиме. Работа с портом производится напрямую - через WinAPI, без каких-либо прослоек/либ.

Вопрос: Как в данном случае приложению быстро и достоверно обнаружить факт пропадания COM-порта из системы? (при отключении USB-устройства например) Каким методом отслеживать факт пропадания порта?Передавать данные в порт по инициативе приложения или менять состояние сигналов (RTS, DTR, etc.) - нельзя (по условию задачи). Периодически закрывать и переоткрывать дескриптор - тоже нельзя.

 

Если продолжать держать COM-порт открытым, то после пропадания COM-порта, его дескриптор остаётся валидным и активным. С ним продолжают корректно работать операции WinAPI чтения данных (возвращают 0 байт) и операции запроса статуса. По результату их выполнения судить о пропадании порта не получается. Если порт был удалён из системы, а потом заново появился, то пока старый дескриптор остаётся открытым, программа не видит данных, идущих из вновь появившегося порта. И не увидит, пока не закроет/переоткроет порт заново. Но как ей узнать, что необходимо произвести такое действие? Имхо - любому, кто работал с виртуальными COM-портами должна быть известна такая проблема.

Создание дубликата дескриптора открытого порта и операции с ним проходят аналогично успешно - так не получается детектировать факт пропадания. Использовать WM_DEVICECHANGE - тоже не хочется (так как типы виртуальных портов могут быть разными; хочется универсального метода). Также не хочется использовать детектирование через SetupAPI (по пропаданию порта из реестра Windows). Ведь порт может пропасть на короткий интервал времени. И заново появиться. И насколько тогда часто нужно сканировать реестр через SetupAPI чтобы поймать этот момент?

Желателен какой-то метод определения, что текущий открытый дескриптор COM-порта протух более не связан с существующим в данный момент COM-портом. Метод работающий без закрытия дескриптора и без отправки каких-либо данных в порт. Пробовал разные функции WinAPI, но не нашёл такой, по выполнению которой можно было бы обнаружить эту ситуацию.

 

В результате размышлений придумал следующий способ: Не закрывая текущий дескриптор, периодически пробовать открывать ещё один дескриптор этого же порта. Тогда:

  • если порт ещё жив и не пропадал из системы: при попытке параллельного открытия возвращается код ошибки "ERROR_ACCESS_DENIED" (так как порт открывался не в shared-режиме);
  • если порт пропал из системы и в данный момент отсутствует: при попытке параллельного открытия возвращается код ошибки "ERROR_FILE_NOT_FOUND" (такого порта нет);
  • если порт пропал из системы и появился заново: его параллельное открытие проходит успешно, без ошибки.

Проверил этот метод на Win7 с USB CDC COM-портом - работает стабильно. Но насколько он надёжен? Нет ли каких-то подводных камней? Будет ли он также надёжно работать в других версия Win? И насколько надёжно полагаться на коды ошибок, возвращаемых функциями WinAPI? Не станут ли в других версиях Win возвращаться другие коды ошибок для описанных состояний? Может лучше комбинировать данный метод со сканированием реестра через SetupAPI?

Кто что думает?

Может кто-то решал подобную задачу, успешно её решил и поделится своим методом? 

Share this post


Link to post
Share on other sites

4 часа назад, jcxz сказал:

В результате размышлений придумал следующий способ

А просто послать туда тестовый пакет и получить или не получить ответ, и соответственно действовать?

Share this post


Link to post
Share on other sites

Только что, HardEgor сказал:

А просто послать туда тестовый пакет и получить или не получить ответ, и соответственно действовать?

Я же ещё в условиях написал:

4 часа назад, jcxz сказал:

Передавать данные в порт по инициативе приложения или менять состояние сигналов (RTS, DTR, etc.) - нельзя (по условию задачи).

 

Share this post


Link to post
Share on other sites

Надо мониторить виндовые USB события с кодами 2100 2102 и проверять их для своего USB порта. Вот только черех какое API это лучше делать?

 

Share this post


Link to post
Share on other sites

25 минут назад, jcxz сказал:

Я же ещё в условиях написал:

Значит надо изменить условия задачи или использовать другой протокол, например HID.

4 часа назад, jcxz сказал:

Имхо - любому, кто работал с виртуальными COM-портами должна быть известна такая проблема.

Известна - эти виртуальные комы - сущий геморрой, и не только со стороны софта, если что, лучше от них отказываться совсем, если нужен интерфейс с компом лучше эзернет, ибо еще и развязку дает, что в промавтоматике точно не помешает))

4 часа назад, jcxz сказал:

Проверил этот метод на Win7 с USB CDC COM-портом - работает стабильно. Но насколько он надёжен?

ИМХО не надежен. С некоторыми переходниками работает так, порт открываю, передаю данные, в процессе отключается физически переходник, потом снова подключается, НО в открытом порту все встает, что логично, и новый порт открыть система не дает, хотя программу закрываю и по закрытии она в норм. состоянии порты закрывает, почему х.з. выдает сообщение "порт уже открыт", и поэтому от такой логики отказался, как в вашем случае, может драйверы другие или еще что, но как-то так...

З.Ы. Может сейчас что-то изменилось, я закончил работу с усб-комами на вин 7, и посл 3 года связь с компом делаю только через эзернет - проблем в разы меньше))

Edited by mantech

Share this post


Link to post
Share on other sites

6 часов назад, jcxz сказал:

Может кто-то решал подобную задачу, успешно её решил и поделится своим методом? 

Самое правильное, с точки зрения системного подхода, это периодический опрос устройства (контрольный обмен). Даже если от внешнего устройства вам, в данный момент, ничего не нужно. А ему нечего вам сообщить. Просто периодический контрольный обмен даннными. Чтобы убедиться, во первых, что канал работает, во вторых, что устройство в рабочем состоянии.

6 часов назад, jcxz сказал:

Передавать данные в порт по инициативе приложения или менять состояние сигналов (RTS, DTR, etc.) - нельзя (по условию задачи).

Вот это и есть ошибка при построении системы. Если нельзя по инициативе приложения, то пусть само устройство периодически шлет контрольные данные, по собственной инициативе. Может у вас уже давно не только порт отвалился, но и само устройство вышло из строя, а вы все ждете ответа...

Share this post


Link to post
Share on other sites

5 часов назад, khach сказал:

Надо мониторить виндовые USB события с кодами 2100 2102 и проверять их для своего USB порта. Вот только черех какое API это лучше делать?

9 часов назад, jcxz сказал:

Вопрос касается виртуальных COM-портов разного рода: как USB CDC, так и разного рода виртуальных COM-портов образованных различными драйверами

Ещё раз: Интересует универсальный метод, подходящий под любые типы COM-портов, а не только USB CDC.

5 часов назад, mantech сказал:

Значит надо изменить условия задачи или использовать другой протокол, например HID.

Ничего менять не нужно. Вопрос про COM-порты и только про COM-порты. Не про HID, не про MSD, не про Ethernet или какой-нить SATA или что там можно ещё нафантазировать. Вопрос про COM-порты. Если кто до сих пор не понял.

5 часов назад, mantech сказал:

порт открываю, передаю данные, в процессе отключается физически переходник, потом снова подключается, НО в открытом порту все встает, что логично, и новый порт открыть система не дает, хотя программу закрываю и по закрытии она в норм. состоянии порты закрывает, почему х.з. выдает сообщение "порт уже открыт"

Как именно ваша программа открывает порты? Какой именно переходник - на каком чипе? Есть уверенность что проблема - не в вашей программе?

Уже проверил работу своего метода (описанного в исходном вопросе) на следующих типах USB COM-портов: FTDI, CP2102, CH340, USB CDC созданный своей программой на МК - всё что было под рукой (4 типа USB портов с разными драйверами). Все эти варианты проверил под Win7 и под WinXP (что было под рукой) - метод везде работает стабильно: факт отключения порта определяется 100% в любом случае, факт нового подключения того-же самого порта не всегда определяется на WinXP+CH340 и не определяется вообще на USB CDC + WinXP. Т.е. - из 8 комбинаций только 2 комбинации работают не полностью. Но возможность определения факта отключения порта - Главная. И она работает 100%. Факт повторного подключения порта в тех случаях, когда он не обнаруживается, можно дожать через SetupAPI. И в любом случае - он менее важен. Как закончатся праздники - проверю метод и на Win11. Если и там будет стабильно работать - видимо метод можно считать рабочим.

Такое поведение как вы описали (что система не даёт открыть пропавший и заново появившийся порт), я наблюдаю только в одной комбинации: WinXP+USB CDC. Под Win7 передёргивал порт много десятков раз, с передачей данных и без - порт заново открывается стабильно всегда. Ни одного случая зависания или неоткрытия не случилось. Были случаи, когда повторное появление порта определялось долго - секунд ~10 или больше. Но чтобы что-то зависло или не открылось - ни разу.

2 часа назад, quаrk сказал:

Самое правильное, с точки зрения системного подхода, это периодический опрос устройства (контрольный обмен). Даже если от внешнего устройства вам, в данный момент, ничего не нужно.

Нет никакого "внешнего устройства". Умерьте буйную фантазию. Есть COM-порт. Речь идёт только о нём. Условия задачи озвучены в первом посте.

Share this post


Link to post
Share on other sites

Вот это Вы написали?

14 часов назад, jcxz сказал:

... приложение ожидает входящих данных, не начиная передачу в порт по своей инициативе.

И от кого ожидает, если

4 часа назад, jcxz сказал:

Нет никакого "внешнего устройства".

???

4 часа назад, jcxz сказал:

Есть COM-порт. Речь идёт только о нём.

СОМ-порт это не "вещь в себе", а канал передачи информации.
Если он задействован в системе, то к нему подключено внешее устройство.
Исправность канала и состояние устройства контролируется периодическим обменом.
Если не задействован, то откладываете его контроль до того, когда он будет задействован.

P.S. Нельзя убедиться в работоспособности канала, не используя его.

Edited by quаrk

Share this post


Link to post
Share on other sites

14 hours ago, jcxz said:

Вопрос: Как в данном случае приложению быстро и достоверно обнаружить факт пропадания COM-порта из системы?

Пример из практики. IDE для ПЛК раз в несколько секунд присылает запрос "ты живой?", на который ПЛК должен ответить за установленное время.

Share this post


Link to post
Share on other sites

@jcxz, вы видели страничку описания https://learn.microsoft.com/en-us/windows/win32/devio/dbt-deviceremovecomplete и пример использования этого сообщения https://learn.microsoft.com/en-us/windows/win32/devio/registering-for-device-notification ?

Мне кажется, что это именно то, что вы ищете. Но риалтаймом здесь и не пахнет.

PS: Да, вы её видели. Пропустил WM_DEVICECHANGE в исходном сообщении.

PPS: Есть и другой, более современный API для этих целей https://learn.microsoft.com/en-us/windows-hardware/drivers/install/registering-for-notification-of-device-interface-arrival-and-device-removal

Цитата

Be sure to handle Plug and Play device events as quickly as possible. If your event handler performs any operation that may block execution (such as I/O), it is best to start another thread to perform the operation asynchronously.

 

Share this post


Link to post
Share on other sites

10 часов назад, jcxz сказал:

Как именно ваша программа открывает порты? Какой именно переходник - на каком чипе?

Программа была готовая, тератерм или что-то подобное, переходник на FT232 и пролифик.

10 часов назад, jcxz сказал:

Были случаи, когда повторное появление порта определялось долго - секунд ~10 или больше.

Возможно у меня такое и было, только дольше, ибо больше 30 сек я не ждал)))

Share this post


Link to post
Share on other sites

11 часов назад, tonyk_av сказал:

Пример из практики. IDE для ПЛК раз в несколько секунд присылает запрос "ты живой?", на который ПЛК должен ответить за установленное время.

Ещё раз: Нет никакого ПЛК, внешнего устройства и т.п. Сколько раз уже можно повторять???

Речь идёт о компоненте (программный компонент; часть программы; библиотека, если кому удобнее), который я использую для разных программ под Win, работающих с COM-портом. Этот компонент имеет среди прочих функцию Open(). Она открывает указанный COM-порт. И далее осуществляется чтение/запись/реакция_на_сигналы/установка_сигналов и т.п. для данного порта через данный компонент пока порт открыт. Также этот компонент умеет уведомлять об асинхронных событиях в COM-порту через соответствующий callback. Хочется в него (в этот компонент) добавить возможность уведомления (через callback) о том, что открытый COM-порт перешёл в состояние "зомби".

Этот компонент я использую в разных программах. Работающих с разными устройствами и по разному. Поэтому - никакой привязки к протоколу, устройству, ПЛК или ещё чему либо на той стороне порта - нет и не может быть. Функция должна работать независимо от протокола работы и от того - что находится на той стороне порта. Никого "опрашивать" она не должна.

Надеюсь сейчас достаточно ясно объяснил?

10 часов назад, makc сказал:

PS: Да, вы её видели. Пропустил WM_DEVICECHANGE в исходном сообщении.

Я хочу функционал, который будет работать независимо от природы виртуального COM-порта - USB, или какой-то другой тип. Для WM_DEVICECHANGE же нужно как я понимаю - каким-то образом получить VID/PID USB из номера порта. Причём - для каждого драйвера (FTDI, CP2102, CH340, ...) видимо по-разному. А для других типов портов - ещё по-другому.

Мне видится это ненадёжным и слишком сложным.

Идеальным вариантом был бы метод, основанный только на функциях Serial WinAPI. Я придумал такой (описал в 1-м посте). Реализация - всего ~13 строк. Very simplest!

10 часов назад, makc сказал:

PPS: Есть и другой, более современный API для этих целей https://learn.microsoft.com/en-us/windows-hardware/drivers/install/registering-for-notification-of-device-interface-arrival-and-device-removal

Очень много читать.... :nea: Если кратко: Он позволяет работать с любыми COM-портами? не зависимо от их природы?

6 часов назад, mantech сказал:

Программа была готовая, тератерм или что-то подобное, переходник на FT232 и пролифик.

Ну вот - есть уверенность, что баги были не в "тератерм или что-то подобное"?

Я в своё время перепробовал множество терминалок. Искал самую безглючную. Не нашёл. :cray: Во всех встречались те или иные баги. Пришлось писать свою. Потому имхо - скорее баг в этом "тератерм или что-то подобное". Тем более что вы и не помните где именно.

А уж про пролифик я вообще промолчу. Про "качество" его работы. Я у себя все переходники на PL23xx уже давно повыкидывал. Чего и вам советую.

Share this post


Link to post
Share on other sites

7 минут назад, jcxz сказал:

Очень много читать.... :nea: Если кратко: Он позволяет работать с любыми COM-портами? не зависимо от их природы?

Вроде да, он работает с Device Handle:

Цитата

After successfully creating a device handle in step 3, call CM_Register_Notification a second time. This time, register for notifications of type CM_NOTIFY_FILTER_TYPE_DEVICEHANDLE, and provide the new device handle as the handle for which to receive notifications. When the device represented by the interface receives a query remove request, the system notifies your component.

 

Share this post


Link to post
Share on other sites

reg query HKLM\HARDWARE\DEVICEMAP\SERIALCOMM

но вероятно только для win10+

 

для XP есть QueryDosDevice("COM1", buff, sizeof(buff));  возможно с \\\\.\\COM10 для портов >10

 

 

 

Share this post


Link to post
Share on other sites

10 минут назад, _pv сказал:

reg query HKLM\HARDWARE\DEVICEMAP\SERIALCOMM

но вероятно только для win10+

Это называется "SetupAPI". О котором я писал в самом начале. Набор функций для работы с реестром Win. И работает это давно - в WinXP уже точно есть. О недостатках его - тоже писал в самом начале. И я его давно использую.

57 минут назад, makc сказал:

Вроде да, он работает с Device Handle:

Надо будет позже почитать.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...