Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Windows7: прием байтов через COM-порт без потерь
Форум разработчиков электроники ELECTRONIX.ru > Cистемный уровень проектирования > Операционные системы
Страницы: 1, 2
Ruslan1
Здравствуйте!
Есть Windows7 Pro, 32-bit, компьютер- китайский одноплатник на Intel 1037U, 4GB RAM.
СОМ-порты- 4 штуки прямо на материнке.
И есть внешний передатчик, посылающий в COM-порт пакеты.
Скорость- 115200, стандартный формат 8N1.
Длина пакета- не более 255 байт, межпакетный интервал- не менее 4 байт, часто гораздо больше (десятки миллисекунд). Каждый пакет имеет контрольную сумму (crc16), по которой и принимается решение о валидности пакета. Общая загрузка канала где-то 5-8 килобайт в секунду, то есть до 80%. Загрузка CPU около 10-15%.

И есть самописная программа на С++Билдере (6), данный вариант делался по прерываниям, с несколькими потоками (базой был вот этот документ).
Есть поток, принимающий все байты по прерываниям и валящий в большой кольцевой буфер. И другой поток периодически выгребает байты из буфера и делит на пакеты для обработки, проверяет валидность.

Только прием, никаких переключений на передачу.
В результате приемник иногда пропускает байты. То есть все принятые байты всегда совпадают с переданными, но некоторые байты пропущены. Всегда пропущено не более одного байта за раз, в любом месте пакета. Часто бывает что пропущено по одному байту в двух следующих друг за другом пакетах.

Обычно фактов потери байта где-то 10-20 в сутки. Корреляция с действиями Виндоуса пока не найдена, очень уж все случайно.
Потери именно в компьютере- подключенный прямо к этому же разъему логический анализатор исправно ловит все байты, никакого криминала или отклонений во времянке не обнаружено (по уровню тоже все без проблем)


Вопросов два:
1. Кто-то в подобных условиях добивался абсолютно безошибочного приема потока через COM-порт в Виндоус (7) на 115200?
2. куда копать? Сильно надеюсь что моя программа виновата. На другом железе пробовал- эффект тот же, то есть это не электроника глючит.
С приоритетами игрался, никакого эффекта.

Заранее спасибо за любые советы (по существу).
AlexRayne
Цитата(Ruslan1 @ May 22 2017, 10:27) *
Есть поток, принимающий все байты по прерываниям и валящий в большой кольцевой буфер. И другой поток периодически выгребает байты из буфера и делит на пакеты для обработки, проверяет валидность.

Вот это место непонятно - для каждого порта свой буфер, или общий на все порты? выпадание байта определяете по сбою crc?

Что Вы называете "приемом по прерыванию"?
У выни свои прекрасные дрова для КОМ, и даже целый специальный апи в ядро встроен под устройства Comm - модемы проще. все известные мне либы в борланде являются прокладкой к этому стандартному АПИ. напрямую с прерыванием никто не работает.
проблем с потерями не было, хотя обмен бывал интенсивный - мегабод.
можно посоветовать покрутить :
1)объем буфферов кома (я делал 8кб)
2)приоритет нитки приемника сделать выше нормального
3)если у вас между пакетами есть пауза более 1мс, настроить ее в свойствах таймингов кома. в запросах асинхронного чтения возможно оно поможет АПИ возвращать целиком пакет а не куски случайно презанные.
4)какая может быть гарантия что порт вашего ПК видит ваш поток без потерь, даже если ваш снифер прекрасен? Вы в качестве снифера тот же самый ПК используете?
5) можно поставить прокси с журналированием на ком (я пользовал com0project, но их вроде множество разных), и уже к нему подключать вашу программу. в этом случае вы сможете сравнить поток принятый прокси с потоком принятым вашей софтиной.
6) вы журналировали поток считанный из порта, сравнить его с финальными нарезанными пакетами? точно эти байты не считываются из порта?
neiver
Начинать надо с того, что COM порт вообще и его реализация в Windows в частности, не гарантирует безошибочной передачи данных.
То есть рассчитывать на это нельзя, и контроль и/или обеспечение целостности данных надо реализовывать в протоколе обмена. Поврежненный пакед должен отбрасываться и если нужно передаваться повторно.
По возможным причинам. У стандартного аппаратного СОМ порта есть внутренний буфер, как правило размером 15 байт. Это не тот буфер, который задается в SetupComm. Если драйвер порта по каким-то причинам не успеет прочитать этот буфер, то принятые байты теряются, о чем извещают соответствующие флаги возвращаемые ClearCommError.
Приоритет пользовательского процесса не влиет на приоритет драйвера СОМ порта при обработке прерываний.
AlexRayne
Цитата(neiver @ May 22 2017, 12:25) *
Начинать надо с того, что COM порт вообще и его реализация в Windows в частности, не гарантирует безошибочной передачи данных.
То есть рассчитывать на это нельзя, и контроль и/или обеспечение целостности данных надо реализовывать в протоколе обмена. Поврежненный пакед должен отбрасываться и если нужно передаваться повторно.

Парень сборется с другой проблемой - у него не пропадание сигнала на линии (этим он займется видимо позже, когда жареный петух клюнет), а пропадание байтов прямо на порте. можно это списать на особенности железа, но вероятность невелика
Ruslan1
AlexRayne, большое спасибо за конструктивный ответ с кучей хороших идей!
Что именно Вы имеете в виде когда говорите про "прекрасные дрова для КОМ, и даже целый специальный апи в ядро встроен под устройства Comm"? Что именно советуете почитать-посмотреть, может даже применительно к простоте прикручивания в Билдер?
Я раньше работал по поллингу, и задачи другие были. А сейчас решил по прерываниям сделать, нашел понятное мне описание в интернете(ссылка в моем письме)- по нему и сделал.

Если уже есть компонент а или просто апишная функция "брать все байты из порта и кидать в большой пользовательский кольцевой буфер" - было бы замечательно. В пятом Билдере ставил пакет TurboPower Async Professional, но использовал по поллингу. Сейчас на новый (хихи) 6-й билдер перешел и думал без сторонних компонентов обойтись.

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

Очень хочется для начала локализовать проблему, и Ваши советы мне отлично подходят- не подумал про независимый сниффер-программу прямо на этой машине. Очень надеюсь что проблема в моем коде- тогда есть большая вероятность ее решения sm.gif
Lagman
Цитата(AlexRayne @ May 22 2017, 11:15) *
Что Вы называете "приемом по прерыванию"?
У выни свои прекрасные дрова для КОМ, и даже целый специальный апи в ядро встроен под устройства Comm - модемы проще. все известные мне либы в борланде являются прокладкой к этому стандартному АПИ. напрямую с прерыванием никто не работает.

Нда, последнее сообщение автора прояснило что прерывание это не то прерывание о котором все подумали, а прерывание есть прерывание потока, по крайней мере так описано по ссылке sm.gif
sonycman
Цитата(neiver @ May 22 2017, 12:25) *
Начинать надо с того, что COM порт вообще и его реализация в Windows в частности, не гарантирует безошибочной передачи данных.
То есть рассчитывать на это нельзя, и контроль и/или обеспечение целостности данных надо реализовывать в протоколе обмена. Поврежненный пакед должен отбрасываться и если нужно передаваться повторно.
По возможным причинам. У стандартного аппаратного СОМ порта есть внутренний буфер, как правило размером 15 байт. Это не тот буфер, который задается в SetupComm. Если драйвер порта по каким-то причинам не успеет прочитать этот буфер, то принятые байты теряются, о чем извещают соответствующие флаги возвращаемые ClearCommError.
Приоритет пользовательского процесса не влиет на приоритет драйвера СОМ порта при обработке прерываний.

Тоже думаю, что проблема просто в переполнении ФИФО аппаратного приёмника.
Потому, что винда тупо не успевает выгребать данные.

Но тогда надо проверить, флаг переполнения должен быть установлен.

Может, стоит попробовать адаптер COM->USB, там аппаратная буферизация может быть лучше, и ошибок не будет?
rx3apf
1. Дурацкий вопрос - а порты вообще с FIFO ? И оно включено ?

2. Как вариант, запустить какую-нибудь терминалку, залоггировать, а потом посмотреть - потери есть ?
Raven
Если Wind'а иногда не успевает выгребать данные из аппаратного буфера, может стоит подумать в сторону аппаратного управления потоком (RTS/CTS)?
DS
В 7 похоже, есть баг в COM драйвере. Многие программы, нормально работавшие с XP, глючат с ком-портами на 7. Единственное, что надежно под ней работает -USB-COM адаптер, причем с FTDI чипом.
ViKo
Я пересылал в комп пакеты данных на скорости 115200, сбоев не замечал. Формат был 8N2. Вот его и посоветую попробовать.
V_G
Цитата(ViKo @ May 23 2017, 15:09) *
Я пересылал в комп пакеты данных на скорости 115200, сбоев не замечал. Формат был 8N2. Вот его и посоветую попробовать.

Да я тоже много чего пересылал, и на 480 кБод, и без сбоев. Насколько я понял, особенность проблем ТС в том, что данные идут постоянно, что в моей практике не встречается.
Хотя я Билдер не пользую, пишу в MSVC++ с отдельным потоком на прием и WaitCommEvent в том потоке с ивентом по каждому пришедшему байту...
ViKo
Возможно, фантазирую, детально не вникал, полагаю, если стопов 2 или 1,5 то синхронизироваться по следующему старту можно точнее. Будет больше запаса на неравенство тактовых частот. А также увеличивается время для работы процессора.
AlexRayne
Цитата(Ruslan1 @ May 22 2017, 16:23) *
AlexRayne, большое спасибо за конструктивный ответ с кучей хороших идей!
Что именно Вы имеете в виде когда говорите про "прекрасные дрова для КОМ, и даже целый специальный апи в ядро встроен под устройства Comm"? Что именно советуете почитать-посмотреть, может даже применительно к простоте прикручивания в Билдер?

Вот это именно и имел ввиду - что вы путаете всех использованием слова "драйвер". В венде уже написаны дрова под множество материнок и уж тем более под известные стандартные контроллеры УАРТ. Вот они как раз напрямую с железом и прерыванием работают. писать их не требуется - они готовы и встроены в Ось. Овь прелагает довольно простое стандартное АПИ для работы с Сомм портами. Если вы поставили борланд - покурите их хелпы Win32 SDK или чтото вроде этого. Это огромный хелп по всем ресурсам венды. и в частности там описан и Comm интерфейс. Если вы полезете в изходники АсинкПро - то наглядно увидите как вендовые вызовы используются. АсинкПро - это обертка вендовых вызовов.
Когда я говорю "прекрасные" - значит они есть работают, отлажены, и проверены. (и удобны)

Цитата(Ruslan1 @ May 22 2017, 16:23) *
AlexRayne, большое спасибо за конструктивный ответ с кучей хороших идей!

Если уже есть компонент а или просто апишная функция "брать все байты из порта и кидать в большой пользовательский кольцевой буфер" - было бы замечательно. В пятом Билдере ставил пакет TurboPower Async Professional, но использовал по поллингу. Сейчас на новый (хихи) 6-й билдер перешел и думал без сторонних компонентов обойтись.

Большой буфер уже предоставляет Сомм интерфейс, размер его можно настраивать, по моей памяти он по умолчанию 4кБ делается. собственно из него и читается данные порта обычными файловыми чтениями.
По моему опыту - оказалось проще работать без оболочек, напрямую. это не требует стороннего кода и возни со сторонними пакетами.
требует освоения навыка работы с асинхронными вызовами венды чтения\записи файла - там их называют почемуто overlaped

Цитата(Ruslan1 @ May 22 2017, 16:23) *
AlexRayne, большое спасибо за конструктивный ответ с кучей хороших идей!
И это не железо, я пробовал на другом компьютере- эффект тоже имеет место быть.
И это действительно потеря в машине- прямо на порт включал независимый лог анализатор, и после сравнивал содержимое буферов его и моей программы.

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

Вам уже посоветовали использовать 1,5-2 стопбита. это хорошее предложение, если оно конечно возможно для ваших абонентов.

С адаптерами КОМ-УСБ советую не играться если есть нормальный порт на матери или PCI плате ввода вывода. Я встретил кривой адаптер от МОХи, народ жаловался на ФТДИ - у обоих проблемы с буфером приемника на больших трафиках.
V_G
Кстати, о больших трафиках. Так ли они необходимы?
Очень много встречал профессиональных программистов, которые для своего личного удобства используют большие скорости и постоянно кидают в порт повторяющиеся данные (типа проверки связи и информации "я живой"). Причем в ситуациях, когда достаточно скорости 9600 и работы по принципу "запрос-ответ". А потом еще переносят эти принципы на радиоканал и забивают все вокруг.
Может, все-таки сначала подумать о минимизации трафика?
Ruslan1
Цитата(ViKo @ May 23 2017, 08:09) *
Я пересылал в комп пакеты данных на скорости 115200, сбоев не замечал. Формат был 8N2. Вот его и посоветую попробовать.

Какое количество байтов в сутки? что такое "сбоев не замечал"? Их не было? Или может быть были, но их никто не логгировал? И, ключевой вопрос- чем принимали (какой софт)?
К сожалению, 8N1.5 или 8N2 не подходит. Как и не подходит "снизить скорость до [тут вставить желаемое]".
ViKo
Я не сутками пересылал, а кадрами. По ним - обработка. Количество байтов на стороне передачи и на стороне приема совпадало. К результатам обработки тоже не в претензии.
Принимал и терминальной программой HTerm и Матлабом.
@Ark
Вероятная причина, все-таки, несовпадение тактовых частот - ошибка более 1%.
Если есть возможность, стоит посмотреть какая тактовая там реально получается в устройстве для скорости 115200.

---
P.S. Кстати, порт компьютера тоже стоит проверить, на всякий случай.
Передавайте в него непрерывный поток нулей на 115200 и посмотрите реальную скорость на выходе.
Ruslan1
Цитата(AlexRayne @ May 23 2017, 12:20) *
Вот это именно и имел ввиду - что вы путаете всех использованием слова "драйвер".

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

Цитата(AlexRayne @ May 23 2017, 12:20) *
В венде уже написаны дрова под множество материнок и уж тем более под известные стандартные контроллеры УАРТ. Вот они как раз напрямую с железом и прерыванием работают. писать их не требуется - они готовы и встроены в Ось. Овь прелагает довольно простое стандартное АПИ для работы с Сомм портами. Если вы поставили борланд - покурите их хелпы Win32 SDK или чтото вроде этого. Это огромный хелп по всем ресурсам венды. и в частности там описан и Comm интерфейс. Если вы полезете в изходники АсинкПро - то наглядно увидите как вендовые вызовы используются. АсинкПро - это обертка вендовых вызовов.


То есть из Ваших слов следует, что предложенный по моей ссылке способ использования винапишных функций негодный, так как Вы советуете мне начинать с нуля и смотреть на другие функции? Я правильно понял?

Я понимаю, что "АсинкПро - это обертка вендовых вызовов", я не знаю каких. Поэтому и спросил, что именно вызывать-то и как это использовать чтобы побороть потерю байтов.
Спасибо, буду читать про Comm.


Цитата(AlexRayne @ May 23 2017, 12:20) *
Вам уже посоветовали использовать 1,5-2 стопбита. это хорошее предложение, если оно конечно возможно для ваших абонентов.

Это не предложение а костыль. Чтобы его применить- нужны аргументы и пояснения, почему "8N1" теряет байты, а вот "8N2" - уже "хорошее предложение". То есть у Винды (или у моего FIFO в железе) есть глюк, полное исправление которого возможно удлинением времени передачи байта на 1 бит?

Цитата(AlexRayne @ May 23 2017, 12:20) *
С адаптерами КОМ-УСБ советую не играться если есть нормальный порт на матери или PCI плате ввода вывода. Я встретил кривой адаптер от МОХи, народ жаловался на ФТДИ - у обоих проблемы с буфером приемника на больших трафиках.

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

Кстати да, просто опять поставить АсинкПро и попробовать из-под него тоже неплохая идея- вряд ли я сделаю общение с виндовыми апи лучше чем они.
Или есть что-то еще уровня Асинк Про для стареньких билдеров?
XVR
Цитата(neiver @ May 22 2017, 11:25) *
У стандартного аппаратного СОМ порта есть внутренний буфер, как правило размером 15 байт. Это не тот буфер, который задается в SetupComm. Если драйвер порта по каким-то причинам не успеет прочитать этот буфер, то принятые байты теряются, о чем извещают соответствующие флаги возвращаемые ClearCommError.
Чтение этого буфера выполняется системным драйвером, причем на самом высоком приоритете (аппаратных прерываний). И что бы он (драйвер) не успел вычитать буфер (причем с учетом того, что этот буфер аппаратный, и у драйвера есть аж целых 15 символьных интервалов, что бы его прочесть), Windows должен быть загружен по самые помидоры rolleyes.gif Да и скорость 115200 не есть что то из ряда вон выходящее.

2 ТС - покажите код вашего потока чтения. И настройки при открытии порта. Сто процентов бага где то там smile3046.gif
ViKo
Посмотрел, сколько стопов использую, оказалось, 1. Раньше было 2. Работает безупречно. Длина передаваемого кадра (непрерывного пакета) до 16 кБ.
Ruslan1
Цитата(XVR @ May 23 2017, 13:20) *
Чтение этого буфера выполняется системным драйвером, причем на самом высоком приоритете (аппаратных прерываний). И что бы он (драйвер) не успел вычитать буфер (причем с учетом того, что этот буфер аппаратный, и у драйвера есть аж целых 15 символьных интервалов, что бы его прочесть), Windows должен быть загружен по самые помидоры rolleyes.gif Да и скорость 115200 не есть что то из ряда вон выходящее.

2 ТС - покажите код вашего потока чтения. И настройки при открытии порта. Сто процентов бага где то там smile3046.gif

Винда не загружена больше ничем, только самой собой sm.gif. Из приложений- еще AVG крутится и Тимвьювер в режиме ожидания входящего соединения. И загрузка CPU 10-20%, обычно меньше.
Я тоже думаю что бага у меня, текст привожу ниже. Текст сильно укороченный, но всю работу с портом оставил как есть.
Нажмите для просмотра прикрепленного файла (это сишный файл)
Проблема- уже в моем кольцевом буфере байтов не хватает.
krux
Цитата(@Ark @ May 23 2017, 14:04) *
Вероятная причина, все-таки, несовпадение тактовых частот - ошибка более 1%.
Если есть возможность, стоит посмотреть какая тактовая там реально получается в устройстве для скорости 115200.

---
P.S. Кстати, порт компьютера тоже стоит проверить, на всякий случай.
Передавайте в него непрерывный поток нулей на 115200 и посмотрите реальную скорость на выходе.

считаю, что расхождение частот компового железа создать таких проблем не может.
Стандарты на ком-порт растут ногами из ITU-T на телеграф лохматых годов, поэтому приемник обязан принимать всё, что отличается по частоте на +- 20%.
Само собой все реализации UART начиная 16550, и заканчивая современными FTDI - им соответствуют. (Хотя за все китайские клоны переходников USB -UART ручаться не могу.)
Ruslan1
Цитата(ViKo @ May 23 2017, 13:31) *
Посмотрел, сколько стопов использую, оказалось, 1. Раньше было 2. Работает безупречно. Длина передаваемого кадра (непрерывного пакета) до 16 кБ.

Понял, спасибо.
У меня потеря раз в час-два, иногда чаще, иногда реже. То есть одна потеря на десяток или более мегабайт траффика. Причем часто ошибки идут в соседних пакетах, может действительно пиковая кратковременная загрузка системы, а у меня что-то не так с буферизацией.
@Ark
Цитата(krux @ May 23 2017, 15:51) *
считаю, что расхождение частот компового железа создать таких проблем не может...

Вы пишите очевидные вещи - так как должно быть. Я же предлагаю ТС проверить, что есть в реальности.
Особенно это касается внешнего устройства. Частоты для UART там обычно получаются делением основной тактовой частоты.
Она далеко не всегда достаточно точно делиться на 115200. Ошибку более 1% можно легко получить, если не смотреть за этим.

Цитата(krux @ May 23 2017, 15:51) *
... приемник обязан принимать всё, что отличается по частоте на +- 20%.

Это не верно.
Чтобы гарантированно без ошибок принимать непрерывный проток - разность частот должна быть не более 1% (по многолетнему опыту).
Все, что выше - с вероятностью сбоев. Тем выше, чем больше разность частот. Разброс 2% - это уже очень плохо, 5% - как правило не работает ничего.
V_G
Цитата(Ruslan1 @ May 23 2017, 22:42) *
Я тоже думаю что бага у меня, текст привожу ниже. Текст сильно укороченный, но всю работу с портом оставил как есть.
Нажмите для просмотра прикрепленного файла (это сишный файл)

1. Не видно, где организуется поток для ввода (CreateThread). Я обычно это делаю в функции открытия главного окна приложения. Если сомневаюсь, открываю поток с приоритетом ABOVE_NORMAL, но чаще всего - с нормальным приоритетом
2. После WaitCommEvent не вызываю WaitForSingleObject: вообще не знаю, что это за функция, как-то обхожусь, хотя тоже задействую структуры OVERLAPPED. В остальном обработка похожая
Дома исходников нет, завтра посмотрю на работе: когда-то давал студентам-дипломникам шаблонные советы по организации обмена по com-порту
XVR
Вызов Synchronize(RxDataProcessing); из нити чтения COM порта тормозит эту самую нить до тех пор, пока RxDataProcessing не вернется. Видимо это может быть долго и байт другой может потеряться.
Более того, совершенно непонятно почему у вас эта самая RxDataProcessing вызывается как из главной формы (по таймеру), так и из COM нити - это явно неправильно.
Далее, при вызове RxDataProcessing из main потока она у вас читает и модифицирует переменные, которые используются в COM потоке (RxBuff.wrpnt) - это совершенно недопустимо.
В общем вам надо переработать схему передачи прочтенных данных между нитями.

Далее, конструкция if (RxBuff.wrpnt >= RX_RINGBUFF_SIZE) RxBuff.wrpnt = 0; не должна срабатывать никогда. Это переполнение приемного буфера. Надо бы ее отследить и показать пользователю, а не тихо выбрасывать все, что накопилось sm.gif


Далее, результат вызова ClearCommError надо бы проверить на ошибки.

Вызов ReadFile тоже было бы неплохо проверить на успешность (так, на всякий случай)
AlexRayne
Цитата(Ruslan1 @ May 23 2017, 15:17) *
Хм. Обычно я стараюсь следить за тем что говорю и не "путать" людей. Например я ни разу нигде не упоминал слово "Драйвер". Я также дал ссылку на то на базе чего писал софт. И в том документе тоже их программа ни разу нигде не названа драйвером.

Да действительно не используете. видимо слово "прерывание" меня в ступор ввело, и многочисленные упоминания как работает контроллер УАРТА. напрямую с контроллером ,кроме драйвера, ведь венда никому работать не даст.

Цитата(Ruslan1 @ May 23 2017, 15:17) *
То есть из Ваших слов следует, что предложенный по моей ссылке способ использования винапишных функций негодный, так как Вы советуете мне начинать с нуля и смотреть на другие функции? Я правильно понял?

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

Цитата(Ruslan1 @ May 23 2017, 15:17) *
Это не предложение а костыль. Чтобы его применить- нужны аргументы и пояснения, почему "8N1" теряет байты, а вот "8N2" - уже "хорошее предложение". То есть у Винды (или у моего FIFO в железе) есть глюк, полное исправление которого возможно удлинением времени передачи байта на 1 бит?

когда у вас появятся помехи на линии, вы вспомните про этот костыль. потому что 8N1 не имеет никакого признака начала фрейма за исключением качественной синхронизации 2х абонентов. как правило эта синхронизация регулярно обновляется появлением пауз между байтами. но если вы шлете долго долго плотный поток на предельной скорости - нет никакого способа приемнику определить начало байта.

Цитата(Ruslan1 @ May 23 2017, 15:17) *
Кстати да, просто опять поставить АсинкПро и попробовать из-под него тоже неплохая идея- вряд ли я сделаю общение с виндовыми апи лучше чем они.
Или есть что-то еще уровня Асинк Про для стареньких билдеров?

работа с винапи будет не сложнее, У вас все нормально же написано. сделать сильно проще не получится.

Цитата(XVR @ May 23 2017, 18:06) *
Вызов Synchronize(RxDataProcessing); из нити чтения COM порта тормозит эту самую нить до тех пор, пока RxDataProcessing не вернется. Видимо это может быть долго и байт другой может потеряться.
Более того, совершенно непонятно почему у вас эта самая RxDataProcessing вызывается как из главной формы (по таймеру), так и из COM нити - это явно неправильно.
Далее, при вызове RxDataProcessing из main потока она у вас читает и модифицирует переменные, которые используются в COM потоке (RxBuff.wrpnt) - это совершенно недопустимо.
В общем вам надо переработать схему передачи прочтенных данных между нитями.

+1
Все еще хуже Synchronize тут использовать это жупел. он делает блокируеще исполнение RxDataProcessing в нитке VCL - когда все формочки перерисовываются. это тормоз который трудно описать.
используйте сигналы (Event) или семафоры чтобы передавать другой нитке ваш буфер.
да и обработку этого буфера лучше перенести сюда из нитки VCL , и отдавать уже гтовые распознаные и провереные пакеты.
Ruslan1
Большое спасибо всем за кучу отличных идей и советов, буду разбираться!

Спасибо за замечание про Synchronize, именно по озвученным причинам я этот метод и не использую (вначале использовал, но в процессе борьбы с теряющимися байтами перестал). Сильно извиняюсь что не удалил из текста неиспользуемые строки. Они там просто закомментарены в том исходнике который вы смотрите, и снабжены комментарием:
Цитата
//the method is changed, just periodic polling in main task by a timer
// Synchronize(RxDataProcessing);


То есть эта нитка сама по себе и валит в глобальный буфер, не останавливаясь на этот Synchronize().

Цитата(XVR @ May 23 2017, 16:06) *
Вызов Synchronize(RxDataProcessing); из нити чтения COM порта тормозит эту самую нить до тех пор, пока RxDataProcessing не вернется. Видимо это может быть долго и байт другой может потеряться.

там "//" перед этим, есть строка не используется.

Цитата(XVR @ May 23 2017, 16:06) *
Далее, при вызове RxDataProcessing из main потока она у вас читает и модифицирует переменные, которые используются в COM потоке (RxBuff.wrpnt) - это совершенно недопустимо.

О!!
вот тут может и есть мой ошибк.
Я думал что достаточно их как volatile объявить и использовать.

Цитата(XVR @ May 23 2017, 16:06) *
В общем вам надо переработать схему передачи прочтенных данных между нитями.

дада, спасибо, буду думать. А как без Synchronize это сделать? неужто через глобальные переменные нельзя?
Просто скажите в каком направлении копать, я копать умею только где не знаю....
(Upd: извиняюсь, уже прочитал совет про евенты и семафоры, значит через них буду. Тогда уже сразу про очереди поищу, чтоб просто мессадж с новым указателем записи передать в ожидающую нитку- надеюсь оно тут есть где-то).

Цитата(XVR @ May 23 2017, 16:06) *
Далее, конструкция if (RxBuff.wrpnt >= RX_RINGBUFF_SIZE) RxBuff.wrpnt = 0; не должна срабатывать никогда. Это переполнение приемного буфера. Надо бы ее отследить и показать пользователю, а не тихо выбрасывать все, что накопилось sm.gif

У меня классический кольцевой буфер, в который один процесс что-то записывает по указателю записи, другой процесс -что-то читает по указателю чтения. Само собой, когда-то доходим до конца буфера и должны следующий байт в его начало записать. Я не понял Вашу идею про "никогда". Указатель записи доходит до RX_RINGBUFF_SIZE периодически, каждые RX_RINGBUFF_SIZE байт.
XVR
Через Synchronize можно, но в нем должно выполняться минимум действий - перекладывание данных из буфера приемного потока в буфер главной формы и немедленный возврат. Так же надо при открытии COM порта задать внутренние буфера системы на COM порт побольше, что бы при вызовах Synchronize данные не терялись, а складывались в этот буфер. Если все же будет не хватать времени, то можно подумать и об отказе от Synchronize

Далее, в нити приема нужно делать не кольцевой буфер, а прием пакета (например в массив). Потом полностью принятый пакет (нарезанный по его границе) внутри Synchronize копируется в AnsiString и засовывается в очередь (например в std::deque<AnsiString>). Извлечение из очереди делается по таймеру.

Этот подход хорош тем, что он весьма простой, и оставляет много возможностей для оптимизации (начиная от собственного менеджера памяти, что бы не тягать данные через AnsiString и кончая собственной асинхронной очередью без привлечения Synchronize)

У меня даже такая очередь есть (была сделана в качестве примера для лекций по программированию)
CODE
//---------------------------------------------------------------------------

#ifndef queueH
#define queueH
//---------------------------------------------------------------------------
#include <deque>

template<class Item>
class WQueue {
std::deque<Item> queue;

CRITICAL_SECTION cs;
HANDLE sema;
HANDLE not_emp_sig;

public:
WQueue(int max_size)
{
sema=CreateSemaphore(NULL,max_size,max_size,NULL);
not_emp_sig=CreateEvent(NULL,FALSE,FALSE,NULL);
InitializeCriticalSection(&cs);
}
~WQueue()
{
CloseHandle(sema);
CloseHandle(not_emp_sig);
DeleteCriticalSection(&cs);
}

void put(const Item& it)
{
WaitForSingleObject(sema,INFINITE);
EnterCriticalSection(&cs);
queue.push_back(it);
SetEvent(not_emp_sig);
LeaveCriticalSection(&cs);
}

Item get()
{
for(;;)
{
WaitForSingleObject(not_emp_sig,INFINITE);
EnterCriticalSection(&cs);
if (queue.empty())
{
LeaveCriticalSection(&cs);
continue;
}
Item rv=queue.front();
queue.pop_front();
if (!queue.empty()) SetEvent(not_emp_sig);
ReleaseSemaphore(sema,1,NULL);
LeaveCriticalSection(&cs);
return rv;
}
}

size_t get_cur_size()
{
EnterCriticalSection(&cs);
size_t rv=queue.size();
LeaveCriticalSection(&cs);
return rv;
}

};

#endif
Как раз от BCB6.0

Только семафор оттуда удалите - он был сделан для ограничения максимального размера очереди
AlexRayne
Цитата(Ruslan1 @ May 23 2017, 20:13) *
дада, спасибо, буду думать. А как без Synchronize это сделать? неужто через глобальные переменные нельзя?
Просто скажите в каком направлении копать, я копать умею только где не знаю....
(Upd: извиняюсь, уже прочитал совет про евенты и семафоры, значит через них буду. Тогда уже сразу про очереди поищу, чтоб просто мессадж с новым указателем записи передать в ожидающую нитку- надеюсь оно тут есть где-то).

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

Цитата(Ruslan1 @ May 23 2017, 20:13) *
У меня классический кольцевой буфер, в который один процесс что-то записывает по указателю записи, другой процесс -что-то читает по указателю чтения. Само собой, когда-то доходим до конца буфера и должны следующий байт в его начало записать. Я не понял Вашу идею про "никогда". Указатель записи доходит до RX_RINGBUFF_SIZE периодически, каждые RX_RINGBUFF_SIZE байт.

С RX_RINGBUFF_SIZE - у вас все правильно, но переполнение кольцевого буфера вы действительно не контролируете. некрасиво это.
я ушел от кольцевого буфера, ибо гемора с ним много - следить где голова где хвост.
использую линейный буфер - в котором надо накопить целый пакет. как только пакет набрался - отдаю его, а остаток буфера перемащаю в начало.
такая схема позволяет использовать чтение с заданием требуемой длинны, а не как у вас сейчас - ожидание любого события. событий летит много и часто, поэтому лучше использовать блокирующее чтение, и задать длину пакета. буфер в этом случае нужен чтобы проконтроливать качество полученного из порта, и откинуть ненужный мусор если он прилетит.
AHTOXA
Добавлю в копилку способов передачи данных от потока приёма к основному потоку: PostMessage().
Эта функция помещает сообщение в очередь на обработку и тут же возвращает управление. А уж когда главный поток его заберёт и обработает - это его дело.
Я делал так: в потоке приёма создавал на куче пакет, собирал его, проверял валидность, потом отправлял главному окну указатель на этот пакет при помощи PustMessage(). Главный поток обрабатывал пакет и удалял его.
Всё очень просто, надёжно, никто никого не ждёт, и не надо париться с синхронизацией.
AlexandrY
Цитата(Ruslan1 @ May 22 2017, 09:27) *
Здравствуйте!
Есть Windows7 Pro, 32-bit, компьютер- китайский одноплатник на Intel 1037U, 4GB RAM.
СОМ-порты- 4 штуки прямо на материнке.
И есть внешний передатчик, посылающий в COM-порт пакеты.

Заранее спасибо за любые советы (по существу).


Насколько вижу ни Intel 1037U ни его Platform Controller Hub (PCH) не имеют UART-ов.
Значит ваши UART-ы виртуальные через USB.
Я бы посмотрел что у вас еще на USB висит (камера, Wi-Fi...) и поотключал бы их жестким сносом драйверов из системы.
AlexRayne
Напишите хоть в чем выявился источник потерь
Ruslan1
Цитата(AlexandrY @ May 24 2017, 09:35) *
Насколько вижу ни Intel 1037U ни его Platform Controller Hub (PCH) не имеют UART-ов.
Значит ваши UART-ы виртуальные через USB.
Я бы посмотрел что у вас еще на USB висит (камера, Wi-Fi...) и поотключал бы их жестким сносом драйверов из системы.

Спасибо, интересная идея, не подумал про такое.
Сериальные порты в BIOS Setup уже видны, или по этому признаку не определить через что оно работает? Есть какие-то низкоуровневые тестеры, чтоб подергали железо и сказали через какой чип оно работает и что еще на каком хабе сидит (если это USB)? Машинка используется вот такая. (кстати, эта модель очень понравилась по качеству исполнения)

Но вряд ли дело только в этом, даже если так. Пробовал и с совершенно другим железом -то же самое видел. Думаю, глюки у меня в программе, займусь корректировкой и тестами ближе к выходным, материала и идей тут накидали достаточно, лишь бы время найти sm.gif
AlexRayne
Цитата(Ruslan1 @ May 24 2017, 16:53) *
Спаисибо, интересная идея, не подумал про такое.
Сериальные порты в BIOS Setup уже видны, или по этому признаку не определить через что оно работает? Есть какие-то низкоуровневые тестеры, чтоб подергали железо и сказали через какой чип оно работает и что еще на каком хабе сидит (если это USB)? Машинка используется вот такая. (кстати, эта модель очень понравилась по качеству исполнения)

Но вряд ли дело только в этом, даже если так. Пробовал и с совершенно другим железом -то же самое видел. Думаю, глюки у меня в программе, займусь корректировкой и тестами ближе к выходным, материала и идей тут накидали достаточно, лишь бы время найти sm.gif

А в диспетчере устройств это хозяйство не разглядеть?
Ruslan1
Цитата(AlexRayne @ May 24 2017, 14:52) *
Напишите хоть в чем выявился источник потерь

Я обязательно отпишусь как разберусь/не разберусь, пока руки не дошли, через день-два возьмусь. Приоритеты задач меняются на ходу...
Timmy
В коде меня удивляет использование overlapped WaitCommEvent(). Можно ли сразу вызывать WaitForSingleObject() и GetOverlappedResult(), не проверив по коду возврата WaitCommEvent() и GetLastError(), что WaitCommEvent() ушла в асинхронный режим, как это всегда делается в примерах от Микрософт?
XVR
WaitForSingleObject вызывать можно. Если WaitCommEvent не ушла в wait, то event останется в том состоянии, в котором был. А был он в установленом состоянии.
А вот GetOverlappedResult неизвестно - MSDN на этот счет молчит
jcxz
Цитата(sonycman @ May 22 2017, 15:48) *
Тоже думаю, что проблема просто в переполнении ФИФО аппаратного приёмника.
Потому, что винда тупо не успевает выгребать данные.

Не думайте ибо этого не может быть.
Даже если-б загрузка CPU была == 100%, драйвер COM-порта, работающий на высоком уровне привилегий, всё равно успевал бы всё выгребать. Тем более на такой низкой скорости. Тем более, что и загрузка CPU никакая.

Цитата(AHTOXA @ May 24 2017, 09:07) *
Я делал так: в потоке приёма создавал на куче пакет, собирал его, проверял валидность, потом отправлял главному окну указатель на этот пакет при помощи PustMessage(). Главный поток обрабатывал пакет и удалял его.

Хмм... А очередь сообщений, связанная с окном и её обработка тредом окна гарантирует, что сообщения из этой очереди будут обрабатываться в том же порядке, в котором они постились в очередь? Тем более если постинг идёт из разных тредов?
Я бы на это не рассчитывал.
Да и вроде в случае межпоточной передачи сообщений, PostMessage аналогичен SendMessage-у.

Цитата(DS @ May 22 2017, 21:49) *
В 7 похоже, есть баг в COM драйвере. Многие программы, нормально работавшие с XP, глючат с ком-портами на 7. Единственное, что надежно под ней работает -USB-COM адаптер, причем с FTDI чипом.

Моя программа, написанная много лет назад под WinXP, сейчас работает одновременно с 3-я компортами на Win8, каждый - на скорости 921600 бод (отладочные потоки 3-х устройств). Сейчас это COM-порты - на PCI-карте, раньше были - на USB-UART-ах (FTDI и CP210x), а также - комбинации того и другого.
До этого всё так же работало на Win10 на другом компе. Работает это целыми днями.
Так что проблема 99.9% не в виндовых дровах. А как всегда - в кривых руках написателей этих "многих программ".
Проблемы возникают только с PL230x на высоких скоростях. На всех опробованных виндах. Вот тут явно дело в дровах Prolific.
V_G
Цитата(XVR @ May 25 2017, 21:14) *
WaitForSingleObject вызывать можно. Если WaitCommEvent не ушла в wait, то event останется в том состоянии, в котором был. А был он в установленом состоянии.
А вот GetOverlappedResult неизвестно - MSDN на этот счет молчит

У меня все работает нормально с WaitCommEvent и без WaitForSingleObject. Два подряд ожидания не есть хорошо: тут может быть потенциальная причина пропуска информации.
После того, как WaitCommEvent дождалась события, я просто делаю ResetEvent, проверяю код события, по EV_RXFLAG делаю обработку, по другим либо возвращаюсь к WaitCommEvent, либо закрываю порт (если в основном потоке устанавливаю специальный флаг, сигнализирующий о необходимости закрытия)
AHTOXA
Цитата(jcxz @ May 26 2017, 03:14) *
Хмм... А очередь сообщений, связанная с окном и её обработка тредом окна гарантирует, что сообщения из этой очереди будут обрабатываться в том же порядке, в котором они постились в очередь?

Да, гарантирует:
Цитата
With the exception of the WM_PAINT message, the WM_TIMER message, and the WM_QUIT message, the system always posts messages at the end of a message queue. This ensures that a window receives its input messages in the proper first in, first out (FIFO) sequence.

Цитата(jcxz @ May 26 2017, 03:14) *
Тем более если постинг идёт из разных тредов?

Без разницы.
Цитата(jcxz @ May 26 2017, 03:14) *
Я бы на это не рассчитывал.

А здесь не надо гадать, надо просто читать документацию.
Цитата(jcxz @ May 26 2017, 03:14) *
Да и вроде в случае межпоточной передачи сообщений, PostMessage аналогичен SendMessage-у.

Нет.

ЗЫ. Совершенно очевидно, что вы не разбираетесь в теме ©.
jcxz
Цитата(AHTOXA @ May 26 2017, 09:05) *
Да, гарантирует:
А здесь не надо гадать, надо просто читать документацию.

Читать лень - сейчас это не нужно. Но помнится, что в WinAPI есть функции, позволяющие считывать сообщения из очереди по маске. А значит - в произвольном порядке.

Цитата(AHTOXA @ May 26 2017, 09:05) *
Нет.
ЗЫ. Совершенно очевидно, что вы не разбираетесь в теме ©.

Да ладно! А если внимательнее прочитать описание WinAPI? wink.gif

Цитата(AHTOXA @ May 24 2017, 09:07) *
Я делал так: в потоке приёма создавал на куче пакет, собирал его, проверял валидность, потом отправлял главному окну указатель на этот пакет при помощи PustMessage().

А если надо отправить сообщение не главному окну? Или у Вас всегда приложения только с одним окном? laughing.gif
rudy_b
Тут есть стандартная проблема - после приема пришедшего байта, прежде, чем сбрасывать событие, необходимо проверить не пришел-ли еще один байт и, если пришел - выбрать и его. Если этого не сделать а просто сбросить событие - он будет потерян. Т.е. его приход вызовет второе событие, но оно будет сброшено вместе с первым.

Вероятность лишнего сброса события есть всегда. Нужна атомарная команда сброса события при условии отсутствия принятых байт, но ее нет и все зависит от реализации ОС.
jcxz
Цитата(rudy_b @ May 26 2017, 12:19) *
Вероятность лишнего сброса события есть всегда. Нужна атомарная команда сброса события при условии отсутствия принятых байт, но ее нет и все зависит от реализации ОС.

Проблем нет никаких при грамотном построении алгоритма:
Принимающий тред пишет байты в кольцевой буфер. После каждого обновления содержимого буфера (или не после каждого, а при достижении некоего уровня + по таймауту) посылает нотификацию (оконным сообщением) треду, в котором находится управляющее окно. Управляющее окно, получив нотификацию, читает кольцевой буфер из приёмного треда.
Естественно - парсинг потока байт на пакеты и уж тем более - обработку пакетов, желательно делать уже в треде управляющего окна. Не надо смешивать уровни обработки протокола.
PS: И желательно, без реальной необходимости, избегать передачи указателей на блоки данных в куче между тредами. Как тут советуют некоторые товарищи....
AHTOXA
Цитата(jcxz @ May 26 2017, 15:14) *
Читать лень - сейчас это не нужно. Но помнится, что в WinAPI есть функции, позволяющие считывать сообщения из очереди по маске. А значит - в произвольном порядке.

Читать лень, а пофлудить - не лень? Не в произвольном, а FIFO.
Если вы отправляете сообщения WM_MY_DATA, то они будут извлекаться в том порядке, в котором были отправлены.
Цитата(jcxz @ May 26 2017, 15:14) *
Да ладно! А если внимательнее прочитать описание WinAPI? wink.gif

Если почитаете, то, может, немного начнёте разбираться. Но я сомневаюсь и в этом.
Цитата(jcxz @ May 26 2017, 15:14) *
А если надо отправить сообщение не главному окну? Или у Вас всегда приложения только с одним окном? laughing.gif

Без разницы, какому окну. Любому.
XVR
Цитата(jcxz @ May 26 2017, 13:33) *
Принимающий тред пишет байты в кольцевой буфер. После каждого обновления содержимого буфера (или не после каждого, а при достижении некоего уровня + по таймауту) посылает нотификацию (оконным сообщением) треду, в котором находится управляющее окно. Управляющее окно, получив нотификацию, читает кольцевой буфер из приёмного треда.
Вы не забывайте, что у ТС не микроконтроллер, а вполне взрослая ПС. Все кольцевые буфера уже сделаны в ядре ОС, зачем ему еще один у себя?

Цитата
Естественно - парсинг потока байт на пакеты и уж тем более - обработку пакетов, желательно делать уже в треде управляющего окна. Не надо смешивать уровни обработки протокола.
И лишние тоже вводить не надо. Нарезку на пакеты вполне можно делать там, где идет прием с порта. Это же не прямая дыра в железный порт, там еще ОС и куча драйверов по дороге.
Конечно, нарезку на пакеты надо делать только в том случае если она не зависит от содержимого пакетов и/или текущего глобального состояния. Но это уже клинические случаи biggrin.gif

Цитата
PS: И желательно, без реальной необходимости, избегать передачи указателей на блоки данных в куче между тредами.

Это почему вдруг? Обоснуйте 1111493779.gif
V_G
Цитата(rudy_b @ May 26 2017, 20:19) *
Тут есть стандартная проблема - после приема пришедшего байта, прежде, чем сбрасывать событие, необходимо проверить не пришел-ли еще один байт и, если пришел - выбрать и его.

Нет никаких проблем. Я сбрасываю событие, но считываю не байт, а буфер, в котором после сброса могут быть еще байты. Если между сбросом и чтением буфера придут еще байты (хотя тут уже огромные скорости нужны), породившие новое событие, то следующее чтение этих байтов не вернет - они уже считаны.
jcxz
Цитата(AHTOXA @ May 26 2017, 13:23) *
Если вы отправляете сообщения WM_MY_DATA, то они будут извлекаться в том порядке, в котором были отправлены.

А если другое не WM_MY_DATA?

Цитата(AHTOXA @ May 26 2017, 13:23) *
Если почитаете, то, может, немного начнёте разбираться. Но я сомневаюсь и в этом.

Мне его за Вас читать недосуг. Я то прекрасно знаю, что если исходный тред не имеет окна и связанной с ним обработки очереди сообщений
(а так скорее всего и есть, так как треду, читающему байты из потока COM-порта, окно не нужно), то работа SendMessage() и PostMessage() похожа.
Но я сильно сомневаюсь, что Вам это чем-то поможет. Вы это не читайте, а то вдруг ещё поумнеете! biggrin.gif

Цитата(AHTOXA @ May 26 2017, 13:23) *
Без разницы, какому окну. Любому.

Ну да, так и думал - представления о работе WinAPI и взаимодействии окон, тредов, сообщений Вы не имеете.
Отсюда начинаем думать, что будет если отправите этому "любому" окну ваши сообщения с указателями на буферы, а это окно получит WM_CLOSE (либо будет закрыто иным образом) и обработает его раньше?
Будет утечка памяти. Что и является обычным явлением в "прогах" написанных подобными Вам деятелями.

Цитата(XVR @ May 26 2017, 13:33) *
Вы не забывайте, что у ТС не микроконтроллер, а вполне взрослая ПС. Все кольцевые буфера уже сделаны в ядре ОС, зачем ему еще один у себя?

Затем, что обработка кадра - это не сферический конь в вакууме. Для её работы нужна как правило инфа, находящаяся в контролах управляющего окна.
Так что гораздо удобнее работать с этой инфой в пределах одного треда.
И зачем тогда выносить работу с кадрами в другой тред и поиметь кучу проблем с межпотоковым взаимодействием?
И я не говорю, что "нельзя". Я говорю, что "обычно удобнее" делать это в потоке управляющего окна. А уже байтики принимать/передавать в других тредах.

Цитата(XVR @ May 26 2017, 13:33) *
Но это уже клинические случаи biggrin.gif

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

Цитата(XVR @ May 26 2017, 13:33) *
Это почему вдруг? Обоснуйте 1111493779.gif

Это чревато утечками памяти. И другими чудесами. См. выше - уже обосновал.
V_G
Цитата(jcxz @ May 26 2017, 22:38) *
Мне его за Вас читать недосуг. Я то прекрасно знаю, что если исходный тред не имеет окна и связанной с ним обработки очереди сообщений (а так скорее всего и есть, так как треду, читающему байты из потока COM-порта, окно не нужно), то работа SendMessage() и PostMessage() похожа.

Неправда ваша. Поток, не имеющий окна и принимающий данные с com-порта, использует функцию PostMessage() окну, дескриптор которого имеется. Здесь никакого криминала, PostMessage возвращается сразу же после того, как поместит сообщение в очередь.
А вот функция SendMessage() из потока, не имеющего цикла обработки сообщений, является проблемной, т.к. должна дожидаться квитанции (ответного сообщения).
Это теоретические рассуждения, на практике (а я тоже использую PostMessage) программа с SendMessage для передачи сообщений из функции приема данных по com-поту не работает, проверено.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.