Конференция "WinAPI" » Реализация асинхронного(!) доступа к COM-порту [D7, WinXP]
 
  • pihter © (19.02.13 06:11) [0]
    Задача казалась довольно простой... Но, на деле, оказалась что решение задачи независимой передачи и приема на одном порту - не так проста.

    Проблема в том что функция ReadFile блокирует не только поток (ее ведь можно и вынести в отдельный) но и дескриптор открытого порта. И в это время из другого потока этого же процесса отправить данные через этот же СОМ-порт не получается (используется этот же дескриптор) пока не придет байт на вход для считывания.

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

    Примеры кода пока постить не буду - я их тут уже сотню перепробовал, три дня бьюсь. Заранее спасибо за помощь.
  • sniknik © (19.02.13 08:05) [1]
    из того, что знаю (фискальники,барпринтеры,весы,кассы), работающего через COM-порты, проблема "множественного взаимодействия" разных программ к ним решается COM/DCOM/TCP сервером...
    т.е. производитель предоставляет "драйвер" работающий в общем стиле, и являющийся "сервером"/объектом к которому все клиенты шлют запросы на чтение/запись, работают через него, а уж он "в одно рыло", с организацией очереди, работает непосредственно с COM-портом.

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

    естественно, если кто то не "закрывает за собой", или использует метод "прямого доступа" вместе с COM/DCOM объектом (т.е. одна программа правильно другая нет) то возникают проблемы. очень популярная в ЦТО... если контора работает(/взято на поддержку) с программами разных разработчиков.
  • Вариант (19.02.13 09:44) [2]

    > pihter ©   (19.02.13 06:11)


    Ищем и Читаем  по
    CreateFile - FILE_FLAG_OVERLAPPEВ и как вытекающее во всех функциях  смотрим - а что там за параметр lpOverlapped и что такое OVERLAPPED структура.

    Литература для изучения - MSDN нужные тебе функции,
    Рихтер Дж., Кларк Д.Д. - Программирование серверных приложений
    (работа с потоками, работа с вводом/выводом и пр.),
    Последовательные интерфейсы ПК. Практика программирования (П.Агуров)
  • Вариант (19.02.13 09:56) [3]

    > Вариант   (19.02.13 09:44) [2]

    FILE_FLAG_OVERLAPPEВ=FILE_FLAG_OVERLAPPED
  • Германн © (19.02.13 12:20) [4]

    > Примеры кода пока постить не буду

    А зачем тогда вопрос задавать было?
  • pihter © (26.02.13 06:57) [5]

    > ну или предлагается "метод"/стиль работы - закрывать порт
    > после операции, т.е. выполнил чек в кассе/1С-е обязательно
    > закрой порты за собой, чтобы параллельно работающая программа
    > переводов/платежей на телефоны тоже могла сделать чек...
    >  и тоже закрыть за собой.


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

    Я вобщем-то уже разобрался, решил. Но решение мне не понравилось жутко непредскажуемостью работы. Решил через открытие порта на чтение/запись в основном потоке + создание двух потокув на чтение и запись + использование wait-функций в потоке чтения, чтоб не вызывало блокировку дескриптора порта, пока функция чтения ждет входящий байт, и функция записи в соседнем потоке в это время могла записывать. Плюс обращаю внимание, если кто столкнется с такой же проблемой, на правильную работу с OVERLAPPED-структурой. При открытии порта обязательно она должна упоминаться в параметрах CreateFile там в справке написано как, плюс, при любом вызове любой API-функции, связанной с этими портами, нужно создать OVERLAPPED-структуру, забить нулями, создать событие, потом только передавать ее в API-функцию.

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

    Решено было использовать 2 COM-порта, по отдельному процессу-серверу на каждый (чтение/запись) и общение с главным приложением через сообщения windows. 2 СОМ-порта будут каждый подключаться к COM-USB-переходнику, которые будут подключаться к USB-хабу и от него один USB-кабель в машину. Немного костыльно, но очень надежно.
  • pihter © (26.02.13 07:05) [6]

    > > Примеры кода пока постить не буду
    >
    > А зачем тогда вопрос задавать было?


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


    > А зачем тогда вопрос задавать было?


    А вот придет такой же парень, с такой же бедой, а тут уже написано решение - он раз, как прочитает, да как поймет в какую сторону копать, да каааак разберется сам и опыт приобретет и все такое прочее полезное. Если он точно будет знать что таким путем реализовать можно, я ведь реализовал, ему будет уверенее.
  • sniknik © (26.02.13 08:08) [7]
    > пока будешь открывать/закрывать после каждой буквы - данные потеряются.
    значит тебе бы больше подошёл "общий сервер", тем более, по позже описанному, у тебя все в одной программе. чего стоит выделить 1 поток на работу с COM? остальные обращаются с запросами на запись, а сам поток "обработчик" "генерит наружу" события от чтения (если что прочиталось)...

    OVERLAPPED это конечно хорошо, круто, но сам COM устройство последовательное.

    > подключаться к COM-USB-переходнику, которые будут подключаться к USB-хабу и от него один USB-кабель в машину.
    ээээ... нафига тогда COM? раз, как отсюда понял, работа с USB, он то как раз может работать "параллельно". ну, должен. технология новее на десятки лет.
  • Вариант (26.02.13 11:52) [8]

    > pihter ©   (26.02.13 06:57) [5]


    > Я вобщем-то уже разобрался, решил

    Судя по тому, что пишешь дальше - не разобрался до конца. Если ты открыл порт с флагом FILE_FLAG_OVERLAPPED и правильно пользуешься последующими вызовами функций WriteFile, ReadFile,WaitCommEvent, то никакой блокировки на вызовах этих функций у тебя не будет. Все работу с портом в этом случае можно организовать в одном доп. потоке или даже в основном потоке. Единственные блокирующие в этом случае поток вызовы - это вызовы ожидания событий WaitForXXX или MsgWaitForXXX и т.п.  И то блокируют только до возникновения ожидаемого события(ий), ошибки или истечения тайм-аута... Считать с порта ты действительно можешь и более одного байта, рассчитывать на получение только одного байта можно только при небольших скоростях порта и небольшим временем обработки полученного и то не факт, что твой поток получит немедленно управление, когда придет ожидаемое событие.  Для нормальной работы с портом обычно хватает нормального приоритета процесса и потока.  Концепцию работы с последовательными устройствами и работу с функциями ввода вывода можешь найти в той литературе, которую я давал выше в
    > Вариант   (19.02.13 09:44) [2]
  • Германн © (27.02.13 01:07) [9]

    > Концепцию работы с последовательными устройствами и работу
    > с функциями ввода вывода можешь найти в той литературе,
    > которую я давал выше

    До сих пор нигде ни разу не встречал в литературе подробное описание структуры OVERLAPPED. Что это такое, для чего она нужна, как она используется "внутре" Windows и какие особенности её применения в пользовательской программе. Насколько помню у Агурова по этому поводу вроде тоже почти ноль информации.
  • Вариант (27.02.13 07:53) [10]

    > Германн ©   (27.02.13 01:07) [9]

    Рихтер Дж., Кларк Д.Д. - Программирование серверных приложений, есть глава с названием "Cтруктура OVERLAPPED"
  • Вариант (27.02.13 08:08) [11]

    > Германн ©   (27.02.13 01:07) [9]

    Да и в MSDN даже с предупреждением о наиболее часто встречающейся ошибке дано описание. А примеры кода, варианты различных методик организации ввода вывода у Рихтера описаны очень хорошо, читается "легко", интересно. Легко - это не значит, что сразу все понятно (это кому как), это значит что не тянет спать.
  • Германн © (28.02.13 02:05) [12]

    > Вариант   (27.02.13 07:53) [10]
    >
    >
    > > Германн ©   (27.02.13 01:07) [9]
    >
    > Рихтер Дж., Кларк Д.Д. - Программирование серверных приложений,
    >  есть глава с названием "Cтруктура OVERLAPPED"

    Спасибо, почитаю.
  • NoUser (14.03.13 00:31) [13]
    > OVERLAPPED это конечно хорошо, круто, но сам COM устройство последовательное.
    :)
  • pihter © (22.04.13 09:01) [14]

    > значит тебе бы больше подошёл "общий сервер"

    Да, вне всякого сомнения. Я так и реализовал.


    > тем более, по позже описанному, у тебя все в одной программе

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


    > чего стоит выделить 1 поток на работу с COM?

    Да мне не жалко даже процесс :) даже два процесса, как в итоге оказалось :)


    > ээээ... нафига тогда COM?

    Значит, я смотрю, чтоб вам было проще помочь, пора уже раскрыть мою задачу. Задача такая: своя открытая реализация телеграфного аппарата. Данные по нашей линии передаются со скоростью 50 бод. Это достаточно медленно даже для того чтоб у меня реле успевали переключать все. Данные передаются путем перемены полярности напряжения в линии с двумя стоп-битами и в пятибитной кодировке. Мне один толковый дядька подсказал что он, когда был молодым, делал эмуляцию работы телеграфа через СОМ-порт (правда там был DOS - прямая работа с контроллером) оказалось что СОМ-порт настроить на такую работу очень просто - это один из штатных режимов его работы. Таким образом моя задача свелась к реализации гальвонической развязки линии и компьютера (это я справился) и написанию вот такого сервера взаимодействия с СОМ-портом (это я тоже справился, но плохо, нужна помощь). Нужно еще написать программу-кодировшик и вообще с терминалом и возможностью хранить библиотеку телеграмм/поиска/печати на принтере и т. п. Но это все я осилю без труда, ибо уже сто раз много подобной фигни делал и имею неплохой опыт в этом. Короче - не беда. Я и с API-функциями не в первый раз встречаюсь, майкросовтовскую справку уже вдоль и поперек излазил, говорю же, сначала казалось вообще - раз плюнуть.

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

    то же самое реализовал и на сервере приема, бог уже с ним, пусть плокирует хоть весь процесс, но, когда принял байт - отправляй сообщение главной программе. и все тоже работает, но через несколько байт (когда три, когда - десять) он просто перестает принимать данные на порт! примерами кода сейчас поделюсь
  • pihter © (22.04.13 09:14) [15]
    инициализация

    var
     com_port: string;
     DCB   : TDCB;
     ctm1: TCommTimeouts;



    hPort := CreateFile(PChar(com_port),
                         GENERIC_READ,
                         0, nil,
                         OPEN_EXISTING, 0, 0);
     if hPort = INVALID_HANDLE_VALUE then
     begin
       // Обнаружена ошибка, порт открыть не удалось
       WriteToRcvSrvLog(
         'Ошибка: порт '+com_port+' открыть для чтения не удалось', 2);
       critical_error := true;
     end;

     // 3. Чтение текущих настроек порта
     if not GetCommState(hPort, DCB) then
     begin
       // Обнаружена ошибка, настройки получить не удалось
       WriteToRcvSrvLog(
         'Ошибка: настройки порта '+com_port+' получить не удалось', 2);
       critical_error := true;
     end;
     // 4. Настройки:
     // Скорость обмена

     DCB.BaudRate := 50;//[скорость обмена];
     // Число бит на символ
     DCB.ByteSize := 5;//[размер "байта" при обмене по COM порту - обычно 8 бит];
     // Стоп-биты
     DCB.StopBits := 1;//[константа, определяющая кол-во стопбитов];
     // Четность
     DCB.Parity   := NoParity;//[константа, определяющая режим контроля четности];
    // DCB.Flags := 20625;
     // 5. Передача настроек
     if not SetCommState(hPort, DCB) then
     begin
       // Обнаружена ошибка, настройки установить не удалось
       WriteToRcvSrvLog(
         'Ошибка: настройки порта '+com_port+' установить не удалось', 2);
       critical_error := true;
     end;

     // 6. Настройка буферов порта (очередей ввода и вывода)
     if not SetupComm(hPort, 256, 16) then
     begin
       // Обнаружена ошибка, настройки установить не удалось
       WriteToRcvSrvLog(
         'Ошибка: настройки буферов порта '+com_port+' установить не удалось', 2);
       critical_error := true;
     end;

     // 7. Сброс буфферов и очередей
     if PurgeComm(hPort, PURGE_TXABORT or PURGE_RXABORT or PURGE_TXCLEAR
       or PURGE_RXCLEAR) then
     begin
       // порт открыт
       WriteToRcvSrvLog(
         'Порт '+com_port+' успешно открыт для чтения', 1);

       GetCommTimeouts(hport, ctm1);
       ctm1.ReadIntervalTimeout := 500;
       SetCommTimeouts(hport, ctm1);

       //сообщим главному процессу что сервер готов
       PostMessage(StrToInt(ParamStr(1)),WMAT_RECIV_SERVER_REDY,Handle,0);

       server_redy:= true;
     end
     else begin
       // Обнаружена ошибка, порт открыть не удалось
       WriteToRcvSrvLog(
         'Ошибка: порт '+com_port+' открыть не удалось', 2);
       critical_error := true;
     end;



    чтение

    begin
       // когда совсем нефиг делать сидим и ждем данных от СОМ-порта
      FillChar(Buf, 1, 0);
      FillChar(Ovr, SizeOf(ovr), 0);
      //ovr.hEvent := CreateEvent(nil, True, False, nil);
      i := 0;
      res:=false;
     // while not res do
      if not Windows.ReadFile(hport, Buf, 1, i, @ovr) then
       begin
         // ошибка
         WriteToRcvSrvLog('Ошибка чтения байта из СОМ-порта.', 1);
       end
       else begin
         // удачное чтение байта - отправляем сообщение
         WriteToRcvSrvLog('Удачное чтение байта '+IntToStr(Buf)+' из СОМ-порта.', 0);

         PostMessage(StrToInt(ParamStr(1)),WMAT_RECIV_BYTE, Buf, 0);
       end;
     end;

  • pihter © (22.04.13 09:20) [16]

    > Если ты открыл порт с флагом FILE_FLAG_OVERLAPPED и правильно
    > пользуешься последующими вызовами функций WriteFile, ReadFile,
    > WaitCommEvent, то никакой блокировки на вызовах этих функций
    > у тебя не будет


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


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


    байты приходят очень медленно - штуки 2 в секунду. обработка, как видите, только послать сообщение. все вполне успевается.


    > можешь найти в той литературе, которую я давал выше

    вот за литературу - огромное спасибо, очень ценные книги по теме
  • MBo © (22.04.13 10:08) [17]
    Нет желания использовать готовые библиотеки типа CPort (Dejan Crnila)?
    Если хочется сделать руками, то можно там по крайней мере посмотреть работу с WaitCommEvent c  overlapped
  • pihter © (22.04.13 12:10) [18]
    Значится... докладываю вдогонку.. Нашел книгу, которую мне тут любезно присоветовали. Там все подробно описано, насчет программирования СОМ-портов. И насчет вэит-функций. И вообще - все что нужно. Дак вот, есть там, среди прочего, готовый пример. Где уже все написано. Все, как положено. Подключил я свое устройство, изменил в сорсах частоту на 50, он и отправляет и принимает через один порт - все вообще шикарно, кроме одного - событие прихода байта все равно куда-то теряется. То есть сами байты - все приходят, но байт отправленный ранее, может прочитаться только в момент прихода следующего и так далее - по цепочке. а для моей задачи критично чтоб байт немедленно читался. какие-нибудь идеи? может прямой доступ? кто пробовал?
  • pihter © (22.04.13 12:12) [19]

    > Нет желания использовать готовые библиотеки типа CPort (Dejan
    > Crnila)?

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


    > Если хочется сделать руками, то можно там по крайней мере
    > посмотреть работу с WaitCommEvent c  overlapped

    ох как я их уже насмотрелся, даже снились однажды - клянусь )
  • MBo © (22.04.13 14:26) [20]
    Пять бит данных, два стоповых и произвольную скорость Cport поддерживает.
    Насчет срабатывания на каждый принятый байт - это, возможно, зависит от виндового драйвера порта.

    Вот, например, что про дотнетовский класс SerialPort в MSDN написано:
    The DataReceived event is not guaranteed to be raised for every byte received. Use the BytesToRead property to determine how much data is left to be read in the buffer.
    т.е. склоняют к беззастенчивому поллингу порта
  • Германн © (23.04.13 01:56) [21]

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

    Прямой доступ конечно поможет, но без своего драйвера СОМ-порта не обойтись.
    Что гарантировано поможет, причём без всякой головной боли, то это - микроконтроллер. Он сможет легко принимать те данные, которые
    > передаются путем перемены полярности напряжения в линии
    > с двумя стоп-битами и в пятибитной кодировке

    хотя я это описание не понял.
  • Германн © (23.04.13 02:03) [22]

    > MBo ©   (22.04.13 14:26) [20]
    >
    > Насчет срабатывания на каждый принятый байт - это, возможно,
    >  зависит от виндового драйвера порта.
    >

    Именно драйвер СОМ-порта и определяет когда ему нужно генерить событие EV_RXCHAR. Определяет это он по некоему своему алгоритму в соответствии с таймаутами установленными в DCB.Но имхо и baudrate в том алгоритме учитывается. Хотя лично я на таких низких скоростях никогда не работал.
  • pihter © (23.04.13 05:06) [23]

    > Что гарантировано поможет, причём без всякой головной боли,
    >  то это - микроконтроллер

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


    >  передаются путем перемены полярности напряжения в линии
    >  с двумя стоп-битами и в пятибитной кодировке

    ну е-мое. как в СОМ-порту данные передаются? прямоугольные сигналы 5-12в переменной полярности, то плюс, то минус. стоп-биты нужны для синхронизации
  • pihter © (23.04.13 05:07) [24]

    > Именно драйвер СОМ-порта и определяет когда ему нужно генерить
    > событие EV_RXCHAR

    А если слушать порт в отдельном потоке в блокирующем режиме?
  • Вариант (23.04.13 06:51) [25]
    Давайте начнем с MSDN- а что там про DCB есть?

    А там есть -

    The use of 5 data bits with 2 stop bits is an invalid combination, as is 6, 7, or 8 data bits with 1.5 stop bits.
  • Вариант (23.04.13 07:46) [26]
    А вот посмотрев на код  в
    > pihter ©   (22.04.13 09:14) [15]


    > // Стоп-биты
    >  DCB.StopBits := 1;//[константа, определяющая кол-во стопбитов];
    >

    вижу, что выбирается 1,5 стоповых бит
    (
    ONE5STOPBITS = 1;

    )
    , что нормально. А потому дам  такой совет - начни с настроек порта, определись с тем что ты хочешь и что реально устанавливаешь,проверь, что все функции настройки порта возвращают  не ошибочный результат.
    Если все с настойками  ок, смотрим дальше...
  • pihter © (23.04.13 10:58) [27]
    да с количеством стоп-бит я уже игрался и так и так, но все равно спасибо.

    как видишь, код возврата от каждой API-функции проверяется. ошибок он не выдавал.

    и мой код - это уже второстепенно. у меня уже есть работающий пример, единственная проблема - иногда не генерируется событие прихода байта в порт, что не позволяет их своевременно получать...
  • Вариант (23.04.13 12:37) [28]

    > pihter ©   (23.04.13 10:58) [27]



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


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

    Попробуй может все же компонент, который рекомендовал MBO

    PS:
    Кстати я работал с телеграфным аппаратом через  RS232 вполне успешно.
  • Вариант (23.04.13 12:51) [29]

    > Вариант   (23.04.13 12:37) [28]

    Одним словом, как тут пишут - код в студию...
  • pihter © (23.04.13 13:31) [30]

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

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


    > Кстати я работал с телеграфным аппаратом через  RS232 вполне
    > успешно.

    Я не с телеграфным аппаратом работаю, я сам создаю телеграфный аппарат, а СОМ-порт использую для ввода-вывода данных. причем я даже формат не меняю, просто делаю усилитель в линию (он же - гальваническая развязка)


    > Одним словом, как тут пишут - код в студию...

    Взят из книги, разбросан по нескольким файлам и приделан к элементам интерфейса чтоб быть настраиваемым. Все это не нужно и затрудняет чтение, найду время, все лишнее выкину, оставлю функцию инициализации и функцию приема данных, тогда и опубликую так, а то там черт ногу сломит
  • Вариант (23.04.13 13:54) [31]

    > pihter ©   (23.04.13 13:31) [30]


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


    Причин может быть несколько, без кода -  это гадание на кофейной гуще.

    И да, тебе уже говорили - событие может возникнуть на N - байт одно. А Т может быть равен не только 1
  • Германн © (24.04.13 02:26) [32]

    > pihter ©   (23.04.13 05:06) [23]
    >
    >
    > > Что гарантировано поможет, причём без всякой головной
    > боли,
    > >  то это - микроконтроллер
    >
    > что ты имеешь в виду, не распарсил. Какой микроконтроллер?

    Ты же сам упомянул некий "прямой доступ". Тогда только два варианта. Либо свой драйвер СОМ-порта, либо внешняя железяка на любом микропроцессоре. своя самодельная или купленная - это уже не мне решать.
  • Германн © (24.04.13 02:30) [33]

    > Я не с телеграфным аппаратом работаю, я сам создаю телеграфный
    > аппарат, а СОМ-порт использую для ввода-вывода данных. причем
    > я даже формат не меняю, просто делаю усилитель в линию (он
    > же - гальваническая развязка)
    >

    А ну тогда другое дело. :)
 
Конференция "WinAPI" » Реализация асинхронного(!) доступа к COM-порту [D7, WinXP]
Есть новые Нет новых   [134428   +41][b:0][p:0.004]