Итак, проигнорив тролля, продолжу по делу.
Бодания с искусственным интеллектом всё же не прошли бесполезно. Его советы помогли мне создать новую версию модуля для работы с COM-портами, которую выкладываю здесь ниже. По сравнению с предшествующей версией, здесь два главных улучшения.
Оформление в виде класса позволяет открывать несколько портов одновременно, а не один, как было в старой версии.
Во-вторых, преодолено ограничение на 9 номеров COM-а. Новый модуль открывает все номера. Я не мог это проверить на всех существовавших версиях Windows; возможно на очень древних может остаться проблема с двузначными номерами, но начиная с XP всё уже работает исправно.
Кроме того добавлен мелкий сервис, типа списка имеющихся портов.
Вызывать событие впрямую аппаратным прерыванием не получилось. Думаю, что в VB такое вряд ли возможно. Но удалось довольно похоже имитировать это через быстрый опрос в фоновом цикле. Но сразу же выяснилась странная заморочка: DoEvents почему-то не обрабатывает CheckBox-ы, поэтому во время цикла они перестают реагировать. Впрочем, все остальные кнопки продолжают нормально работать.
Хотя событийная модель теперь возможна, но практика показала, что вполне можно обойтись без использования событий, и в некоторых случаях даже удобнее, что я и рекомендую.
Модуль ориентирован на работу с устройствами, подключенными к последовательному порту (как например Ардуино). Обмен, как правило, происходит в такой последовательности: программа посылает устройству команду из одного или нескольких байт, устройство что-либо делает и посылает отчет о своем новом состоянии, и/или другие данные в ответ.
Отсюда происходят некоторые особенности, с которыми я столкнулся в использовании старого модуля.
Передача с компьютера происходит аппаратно, то есть программа бросает данные в буфер передачи и сразу же переходит к приему ответа. А устройство еще не начало отвечать, потому что даже команда ему еще до конца не передалась. Приходилось везде ставить задержки.
Теперь этого не нужно, я сделал передачу с контролем окончания передачи и прием с ожиданием прихода всех символов. Так что глобальное непрерывное ожидание теперь не очень-то и нужно.
Вот список свойств и методов, которые дает модуль.
.OpenCOM -открытие порта. Аргументом имеет номер порта числового типа (а не строкового), чтобы легко было перебирать в цикле. За ним возможны несколько необязательных аргументов: скорость (по умолчанию 9600), битность, четность и стопы - по умолчанию стандартный режим 8, N, 1. (Лично мне ни разу не потребовался другой).
Если порт открыт - возвращает True, в противном случае False, так что открытие порта можно ставить непосредственно в If, проверяя таким образом доступность порта, и одновременно сразу же открывая его тем самым.
Повторное открытие уже открытого порта не является ошибкой. Порт автоматически корректно закрывается и затем открывается вновь, возможно с другими параметрами и номером.
.ClosePort -закрытие порта. Обязательно должно быть выполнено перед выходом из программы. Поставить в Form_Unload. Попытка закрытия уже закрытого или не открытого порта допустима и не приводит к ошибкам. Аргументов не имеет и ничего не возвращает.
.WritePort - простая передача строки в порт. Аргумент - строка байтов, преобразованных в символы. Не возвращает ничего.
.WritePortWait -то же самое, но выполнение приостанавливается и продолжается после фактического завершения передачи всех байтов.
.ReadPort -прием из порта. Имеет необязательный аргумент: количество принимаемых символов. Если аргумент не указан, считывает всё, что на текущий момент было в буфере приема. Возвращает принятые данные в виде символьной строки. Не выполняет ожидания. Если никаких данных не было принято, возвращает пустую строку сразу.
.WaitReadPort -перед началом приема ожидает время, рассчитанное по количеству символов и скорости приемопередачи. Аргумент - количество принимаемых символов. Аргумент обязательный, так как невозможно сделать расчет для неопределенного количества. Если данных нет, возвращает пустую строку.
.LineReadPort -прием строки, завершенной кодами перевода строки (CrLf). Имеет аргументом таймаут в секундах (против зависания, если перевод строки не появился). Если аргумент нулевой, то прием без ожидания.
Возвращает строку данных с кодом CrLf включительно. Если CrLf нет, возвращает то, что есть.
В случае ошибки все функции устанавливают свое сообщение об ошибке в свойство .LastError, где оно может быть прочитано (кириллица).
Если после выполнения любого из действий ошибки не было, это свойство содержит пустую строку.
Если подключены события, то при ошибках записи вызывается событие _WriteFault
а при ошибках чтения- _ReadFault
В обоих передается строка ErrMsg содержащая то же, что и .LastError .
Открытие порта этих событий не вызывает, так как возвращает результат выполнения в булевском формате.
Свойство .WaitData - ожидание данных.
После кода приравнивания этому свойству значения True, выполнение приостанавливается, входя в цикл бесконечного ожидания прихода данных. Приходом первого же байта в порт, этот цикл завершается и выполнение команд продолжается дальше.
.WaitData не считывает данных, а только ожидает факта их прихода. Считывать после нее любой из команд чтения порта.
Если подключены события, то .WaitData вызывает событие _DataReady . С этим вариантом ожидание можно запустить глобально сразу при запуске программы. Но делать это следует не в Form_Load, так как выполнение приостанавливается и форма тогда не сможет загрузиться.
Запускать ожидание глобально следует в самом конце Form_Activate. Как это правильно сделать можно посмотреть в коде прилагаемой тестовой программы; там события реализованы.
Ожидание можно прервать, установив свойству .WaitData значение False.
.PurgeData -очищает все данные, обнуляет все очереди. Предназначено для защиты от случайно сохранившихся данных, могущих вызвать помехи для программы. Аргументов не имеет и ничего не возвращает.
.ListPorts -массив, содержит список номеров портов, имеющихся в системе. Начальный элемент массива (с индексом 0) содержит общее количество портов и соответствует размеру массива. Если он равен нулю, значит COM-портов на машине нет.
Остальные элементы массива содержат номера портов.
.Tag -свободная переменная универсального типа, не участвующая в выполнении. Предназначена для какого-нибудь временного хранения или передачи данных между подпрограммами.
Лицензия: опенсорс

AS IS, что мог, то сделал; кому не нравится - пусть сделают лучше и выложат здесь ко всеобщему удовольствию
