/////////////////////////////////////////////////////////////////////////////////////// // // Тема.......Программирование COM-порта с использованием win32API и assembler // Автор......black_c0de // Группа.....[The N0b0D1eS] //[tN] // E-mail.....bcodebox@gmail.com // HTTP.......http://nteam.ru/ // DATE.......25.07.03 // // // # Все права принадлежат [The N0b0D1eS] // # любое распространение возможно только с сохранением копирайтов и т.д. и т.п. // // /////////////////////////////////////////////////////////////////////////////////////// В данной статье я поведаю основы и тонкости программирования интерфейса RS-232, в народе известного как последовательный COM-порт персонального компутера, с использованием системных функций win32api и assembler'а. В статье будет выложена информация и куски кода, которые являются результатами бессонных ночей, моего пота и крови, которые были пролиты на msdn, разные доки и факи с целью детального изучения всех моментов и хинтов работы с данным портом 8) *** Содержание *** ------------------ [1] вступление [2] немного об интерфейсе RS-232 [3] низкоуровневый доступ к портам. функции BIOS [4] структуры для работы с com-портом, доступные на уровне win32API [5] win32API функции для работы с com-портом [6] тонкости работы с последовательным com-портом. (+пример) *** Вступление *** ------------------ Данная статья посвящена теме программирования последовательного порта ПК. Данный интерфейс до сих пор очень широко используется, начиная от подключения мышки к компу и заканчивая мощнейшими приборами, которые взаимодействуют с компутером. Данный интерфейс позволяет производить ввод/вывод данных, очень гибкую настройку режима передачи, и хороший контроль состояния порта и ошибок, которые возникают. Поэтому то, как и для чего вы будете использовать данный интерфейс зависит большей частью от вашей фантазии, мне, к примеру, доводилось писать много разных тулз, которые работали с данным интерфейсом. Это: программа для управление ПК посредством инфракрасного сигнала, передаваемого с пульта ДУ на комп. К компу подключается простенькая схемка с инфракрасным приемником и программа уже обрабатывает сигналы, которые поступают на порт с прибора. Также разные тулзы для обработки сигналов с разных приборов. Что еще интеретного бывает, так это чаты, которые работают через com-порты компутеров, соединенных между собой. В общем кто как может так и пользует этот интерфейс 8) ps модемы, кстати, тоже на этот порт вешаются, так что разобравшись с командами вашего модема вы сможете закодить свою програму для работы с модемом 8) *** Немного об интерфейсе RS-232 *** ------------------------------------ Интерфейс RS-232-C разработан ассоциацией электронной промышленности (Electronic Industries Association - EIA) как стандарт для соединения компьютеров и различных последовательных периферийных устройств. Данный интерфейс является асинхронным. Тоесть позволяет одновременное выполнение нескольких операция ввода/вывода. Соответственно средства кодинга порта учитывают это и работа с портом является оч удобной и логичной. В основе порта лежит микросхема Intel 8250 или ее современные аналоги - Intel 16450, 16550, 16550A. Эта микросхема является универсальным асинхронным приемопередатчиком (UART - Universal Asynchronous Receiver Transmitter). В данной микрухе имеются сдвиговый и буферный регистры приемника. Программа имеет доступ только к буферным регистрам, копирование информации в сдвиговые регистры и процесс сдвига выполняется микросхемой UART автоматически. *** Низкоуровневый доступ к портам. Функции BIOS *** ---------------------------------------------------- те, кто кодируют на асме знают что взаимодействие с различными аппаратным средствам ПК осуществляется посредством вызова соответствующего прерывания или функции BIOS. Так вот, наш интерфейс RS-232 (com-порт) тоже может вырабатывать прерывания: COM1, COM3 - IRQ4 (соответствует INT 0Ch) COM2, COM4 - IRQ3 (соответствует INT 0Bh) COM1 имеет базовый адрес 3F8h и занимает диапазон адресов от 3F8h до 3FFh COM2 имеет базовый адрес 2F8h и занимает адреса 2F8h...2FFh COM3 имеет базовый адрес 3E8h и занимает диапазон адресов от 3E8h до 3EFh COM4 имеет базовый адрес 2E8h и занимает адреса 2E8h...2EFh Рассмотрим функции, которые нам доступны для работы с портом: AH=00h - Инициализация порта ---------------------------- AL: смотрите в таблице DX: номер порта(0-3; 0 equ. 0x3f8, 1 equ. 0x2f8, и т.д.) Bit 7 Bit 6 Bit 5 Rate [bps] Bit 4 Bit 3 Parity 1 1 1 9600 0 0 none 1 1 0 4800 1 0 none 1 0 1 2400 0 1 odd 1 0 0 1200 1 1 even 0 1 1 600 0 1 0 300 Bit 1 Bit 0 Data bits 0 0 1 150 0 0 5 0 0 0 110 0 1 6 1 0 7 Bit 2 0 -> 1 stop bit, 1 -> 2 stop bits 1 1 8 (!)Возвращаемые значения: AH: RS-232C бит статуса линии Бит 0: RBF - данные доступны в буфере 1: OE - данные утеряны 5: THRE - room is available in output buffer 6: TEMT - буфер пустой AL: биты статуса модема 3: всегда 1 7: DCD - несущая AH=01h - Записать байт ---------------------- AL: символ для посылки в порт DX: порт (!)Возвращаемые значения: AH: 7-й бит сброшен - все ок, установлен - возникла ошибка. Биты 0-6 смотрите INT 14h AH=03h AH=02h - Прочитать байт ----------------------- (!)Возвращаемые значения: AH: Состояние линии (смотрите AH=03h) AL: принятый символ (если 7-й бит AH не установлен) AH=03h - получить информацию о статусе порта -------------------------------------------- DX: Порт (!)Возвращаемые значения: AH: статус линии Bit 7: Timeout Bit 6: TEMT Transmitter empty Bit 5: THRE Transmitter Holding Register Empty Bit 4: Break (broken line detected) Bit 3: FE Framing error Bit 2: PE Parity error Bit 1: OE Overrun error Bit 0: RDF Receiver buffer full (data available) AL: Modem Status Bit 7: DCD Carrier detect Bit 6: RI Ring indicator Bit 5: DSR Data set ready Bit 4: CTS Clear to send Bit 3: DDCD Delta carrier detect Bit 2: TERI Trailing edge of ring indicator Bit 1: DDSR Delta data set ready Bit 0: DCTS Delta Clear to send *** структуры для работы с com-портом, доступные на уровне win32API *** ----------------------------------------------------------------------- win32api предоставляют нам пять структур для работы с com-портом. структуры для работы с com-портом объявлены в заголовочном файле winbase.h COMMCONFIG - предоставляет информацию о конфигурации комуникационного устройства члены структуры: ---------------- DWORD dwSize; /* Size of the entire struct */ WORD wVersion; /* version of the structure */ WORD wReserved; /* alignment */ DCB dcb; /* device control block */ DWORD dwProviderSubType; /* ordinal value for identifying provider-defined data structure format*/ DWORD dwProviderOffset; /* Specifies the offset of provider specific data field in bytes from the start */ DWORD dwProviderSize; /* size of the provider-specific data field */ WCHAR wcProviderData[1]; /* provider-specific data */ COMMPROP - структура, заполняемая при вызове GetCommProperties (), предоставляет информацию о комуникацонном драйвере члены структуры: ---------------- WORD wPacketLength; WORD wPacketVersion; DWORD dwServiceMask; DWORD dwReserved1; DWORD dwMaxTxQueue; DWORD dwMaxRxQueue; DWORD dwMaxBaud; DWORD dwProvSubType; DWORD dwProvCapabilities; DWORD dwSettableParams; DWORD dwSettableBaud; WORD wSettableData; WORD wSettableStopParity; DWORD dwCurrentTxQueue; DWORD dwCurrentRxQueue; DWORD dwProvSpec1; DWORD dwProvSpec2; WCHAR wcProvChar[1]; COMMTIMEOUTS - определяет значения таймаутов для операций ввода/вывода. используются функции SetCommTimeouts() и GetCommTimeouts() члены структуры: ---------------- DWORD ReadIntervalTimeout; /* Maximum time between read chars. */ DWORD ReadTotalTimeoutMultiplier; /* Multiplier of characters. */ DWORD ReadTotalTimeoutConstant; /* Constant in milliseconds. */ DWORD WriteTotalTimeoutMultiplier; /* Multiplier of characters. */ DWORD WriteTotalTimeoutConstant; /* Constant in milliseconds. */ COMSTAT - возвращает информацию о текущем состоянии последовательного порта, заполняется при вызове функции ClearCommError() члены структуры: ---------------- DWORD fCtsHold : 1; DWORD fDsrHold : 1; DWORD fRlsdHold : 1; DWORD fXoffHold : 1; DWORD fXoffSent : 1; DWORD fEof : 1; DWORD fTxim : 1; DWORD fReserved : 25; DWORD cbInQue; DWORD cbOutQue; DCB - содержит основные установки комуникационного устройства. для получения структуры используйте функцию GetCommState() члены структуры: ---------------- DWORD DCBlength; /* sizeof(DCB) */ DWORD BaudRate; /* Baudrate at which running */ DWORD fBinary: 1; /* Binary Mode (skip EOF check) */ DWORD fParity: 1; /* Enable parity checking */ DWORD fOutxCtsFlow:1; /* CTS handshaking on output */ DWORD fOutxDsrFlow:1; /* DSR handshaking on output */ DWORD fDtrControl:2; /* DTR Flow control */ DWORD fDsrSensitivity:1; /* DSR Sensitivity */ DWORD fTXContinueOnXoff: 1; /* Continue TX when Xoff sent */ DWORD fOutX: 1; /* Enable output X-ON/X-OFF */ DWORD fInX: 1; /* Enable input X-ON/X-OFF */ DWORD fErrorChar: 1; /* Enable Err Replacement */ DWORD fNull: 1; /* Enable Null stripping */ DWORD fRtsControl:2; /* Rts Flow control */ DWORD fAbortOnError:1; /* Abort all reads and writes on Error */ DWORD fDummy2:17; /* Reserved */ WORD wReserved; /* Not currently used */ WORD XonLim; /* Transmit X-ON threshold */ WORD XoffLim; /* Transmit X-OFF threshold */ BYTE ByteSize; /* Number of bits/byte, 4-8 */ BYTE Parity; /* 0-4=None,Odd,Even,Mark,Space */ BYTE StopBits; /* 0,1,2 = 1, 1.5, 2 */ char XonChar; /* Tx and Rx X-ON character */ char XoffChar; /* Tx and Rx X-OFF character */ char ErrorChar; /* Error replacement char */ char EofChar; /* End of Input character */ char EvtChar; /* Received Event character */ WORD wReserved1; /* Fill for now. */ ну и можно добавить две структуры для работы с модемами, которые объявлены в mcx.h: MODEMDEVCAPS - структура содержит информацию об аппаратных возможностях модема члены структуры: ---------------- DWORD dwActualSize; DWORD dwRequiredSize; DWORD dwDevSpecificOffset; DWORD dwDevSpecificSize; // product and version identification DWORD dwModemProviderVersion; DWORD dwModemManufacturerOffset; DWORD dwModemManufacturerSize; DWORD dwModemModelOffset; DWORD dwModemModelSize; DWORD dwModemVersionOffset; DWORD dwModemVersionSize; // local option capabilities DWORD dwDialOptions; // bitmap of supported values DWORD dwCallSetupFailTimer; // maximum in seconds DWORD dwInactivityTimeout; // maximum in seconds DWORD dwSpeakerVolume; // bitmap of supported values DWORD dwSpeakerMode; // bitmap of supported values DWORD dwModemOptions; // bitmap of supported values DWORD dwMaxDTERate; // maximum value in bit/s DWORD dwMaxDCERate; // maximum value in bit/s // Variable portion for proprietary expansion BYTE abVariablePortion [1]; MODEMSETTINGS - информация об настройках и установках модема члены структуры: ---------------- DWORD dwActualSize; DWORD dwRequiredSize; DWORD dwDevSpecificOffset; DWORD dwDevSpecificSize; // static local options (read/write) DWORD dwCallSetupFailTimer; // seconds DWORD dwInactivityTimeout; // seconds DWORD dwSpeakerVolume; // level DWORD dwSpeakerMode; // mode DWORD dwPreferredModemOptions; // bitmap // negotiated options (read only) for current or last call DWORD dwNegotiatedModemOptions; // bitmap DWORD dwNegotiatedDCERate; // bit/s // Variable portion for proprietary expansion BYTE abVariablePortion [1]; *** win32API функции для работы с com-портом *** ------------------------------------------------ win32api предоставляет множество функций для работы с устройствами связи и последовательными портами ввода вывода. благодарю такому набору средств можно писать очень эфективные програмы для работы с портами, етц. далее я приведу перечень все функция с описание функции, также укажу параметры функции и их типы. более детальную информацию о параметрах функций и самих функциях вы можете найти в MSDN'е (http://msdn.microsoft.com) или же в других источниках информации. если я стану описывать все нюансы касательно каждой функции и ее аргумента - статья превратится в настоящую книгу объемом порядка сотни страниц. да и имена параметров достаточно понятны чтобы описывать каждый отдельно ;) BuildCommDCB - Заполняет указанную структуру блока описания устройств значениями, указанными в строке управления устройства. BOOL BuildCommDCB( LPCTSTR lpDef, LPDCB lpDCB ); BuildCommDCBAndTimeouts - Переводит строку определения устройства в соответствующие коды управляющего блока устройства и размещает их в управляющий блок устройства. BOOL BuildCommDCBAndTimeouts( LPCTSTR lpDef, LPDCB lpDCB, LPCOMMTIMEOUTS lpCommTimeouts ); ClearCommBreak - Передача символа восстановлений для указанного устройства связи. Сбрасывает значения ошибок и заполняет структуру COMSTAT. BOOL ClearCommBreak( HANDLE hFile ); ClearCommError - Вовращает информацию об ошибках связи и сообщает о текущем состоянии устройства связи. BOOL ClearCommError( HANDLE hFile, LPDWORD lpErrors, LPCOMSTAT lpStat ); CommConfigDialog - Отображает снабженное драйвером диалоговое окно конфигурации. BOOL CommConfigDialog( LPCTSTR lpszName, HWND hWnd, LPCOMMCONFIG lpCC ); EscapeCommFunction - Указывает указанному устройство связи, что необходимо выполнить расширенную функцию. BOOL EscapeCommFunction( HANDLE hFile, DWORD dwFunc ); GetCommConfig - Возвращает текущую конфигурацию устройства связи BOOL GetCommConfig( HANDLE hCommDev, LPCOMMCONFIG lpCC, LPDWORD lpdwSize ); GetCommMask - Возвращает значение маски события для указанного устройства связи. BOOL GetCommMask( HANDLE hFile, LPDWORD lpEvtMask ); GetCommModemStatus - Возвращает модемные значения регистра управления. BOOL GetCommModemStatus( HANDLE hFile, LPDWORD lpModemStat ); GetCommProperties - Возвращает информацию о свойствах связи для указанного устройства связи. BOOL GetCommProperties( HANDLE hFile, LPCOMMPROP lpCommProp ); GetCommState - Возвращает текущие параметры настройки управления для указанного устройства связи. BOOL GetCommState( HANDLE hFile, LPDCB lpDCB ); GetCommTimeout - Возвращает параметры блокировки времени для всего чтения и операций записи на указанном устройстве связи. BOOL GetCommTimeouts( HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts ); GetDefaultCommConfig - Возвращает заданную по умолчанию конфигурацию для указанного устройства связи. BOOL GetDefaultCommConfig( LPCTSTR lpszName, LPCOMMCONFIG lpCC, LPDWORD lpdwSize ); PurgeComm - Сбрасывает все символы вывода или входного буфера указанного ресурса связи. BOOL PurgeComm( HANDLE hFile, DWORD dwFlags ); SetCommBreak - Передача символа Suspends для указанного устройства связи и установка состояния break до вызова ф-ции ClearCommBreak() BOOL SetCommBreak( HANDLE hFile ); SetCommConfig - Устанавливает текущую конфигурацию устройства связи. BOOL SetCommConfig( HANDLE hCommDev, LPCOMMCONFIG lpCC, DWORD dwSize ); SetCommMask - Определяет набор событий, которые будут зафиксированы для устройства связи. BOOL SetCommMask( HANDLE hFile, DWORD dwEvtMask ); SetCommState - Конфигурирует устройство связи согласно спецификациям в управляющем блоке устройства. BOOL SetCommState( HANDLE hFile, LPDCB lpDCB ); SetCommTimeouts - Устанавливает параметры блокировки времени для всего чтения и операций записи на указанном устройстве связи. BOOL SetCommTimeouts( HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts ); SetDefaultCommConfig - Устанавливает заданную по умолчанию конфигурацию для устройства связи. BOOL SetDefaultCommConfig( LPCTSTR lpszName, LPCOMMCONFIG lpCC, DWORD dwSize ); SetupComm - Инициализирует параметры связи для указанного устройства связи. BOOL SetupComm( HANDLE hFile, DWORD dwInQueue, DWORD dwOutQueue ); TransmitCommChar - Передает указанный символ перед любыми операциями ожидания данных в буфере вывода указанного устройства связи. BOOL TransmitCommChar( HANDLE hFile, char cChar ); WaitCommEvent - Ожидает события, которые установлены с помощью SetCommMask() BOOL WaitCommEvent( HANDLE hFile, LPDWORD lpEvtMask, LPOVERLAPPED lpOverlapped ); *** тонкости работы с последовательным com-портом *** ----------------------------------------------------- ну вот теперь вы в курсе что такое com-порт и какими средствами мы владеем для программирования данного интерфейса. начнем мы с простого примера и одновременно самой распространенной операции - чтение данных из порта. итак, приступим. как мы уже сказали, при работе с портом на ассемблере мы используем соответствующие функции BIOS, выше я уже перечислил их. для чтения и записи в порт в асме есть две простые команды: in аккумулятор, номер_порта ; ввод аккумулятора из порта с номером номер_порта out порт, аккумулятор ; вывод содержимого аккумулятора в порт с номером номер_порта тут все просто как двери. загнали в регистры нужные значения, вызвали функцию, потом прерывание и вперед - анализируем значения, которые устанавливаются после выполнения определенной функции. это все было описано выше. так как основное внимание в данной статье уделено кодингу с использованием win32api функций - на асме останавливаться не будем, так как на нем это все делается гораздо проще нежели многие думают, там нет пары десятков функций и структур, асм прост, как говорят - все гениальное просто. так и асм. ну да ладно, если кто считает, что кодинг на асме это изврат пускай и дальше заблуждается и верит в свое ограничивающее убеждение ;)) итак. с асмом все понятно 8) а как быть с api функциями? с ними тоже все просто, нужно одинраз разобраться и все станет понятно. для начала необходимо проинициализировать порт, для этого используется функция CreateFile() char *port_ptr = "COM1"; HANDLE hCom = CreateFile(port_ptr, // init port GENERIC_READ | GENERIC_WRITE, // read/write access FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,// default security attribute OPEN_EXISTING, //COM's must exist 8-) FILE_FLAG_OVERLAPPED, //use for overlapped i/o. 0 ); единственное что стоит сказать относителньо использования этой функции для инициализации порта - не игнорируйте шестой параметр и устанавливайте его FILE_FLAG_OVERLAPPED. это укажет функции, что необходимо инициализировать порт в режиме асинхронного ввода/вывода. что это значит? к примеру вы написали програмку, которая читает данные из порта, анализирует их. что-то еще делает, с окошками, менюшками, разными фичами. так вот. если работать с портом в режиме синхронного ввода/вывода - поток, в котором происходит обращение к ресурсам порта блокируется до появления событий порта, а в режиме асинхронного вв/в - операция ожидания переводится в фоновый режим выплнения и вы можете в том же потоке делать что вам необходимо тем самым не мешая программе ожидать событий от порта... // итак, инициализируем порт, проверяем результат выполнения функции: if( hCom != INVALID_HANDLE_VALUE ) printf("(+) %s initialized\n", port_ptr); else printf("(-) %s initialization failed\n", port_ptr); // теперь нужно сбросить значения буферов порта и регистров: int retcode = PurgeComm(hCom, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR); // retcode будет содержать код возврата if( retcode==NULL ) printf("(-) PurgeComm() failed\n"); else printf("(+) Discarded all characters from the output and input buffer\n"); retcode = ClearCommBreak(hCom); if( retcode==NULL ){ printf("(-) ClearCommBreak() failed\n"); PrintLastError(); }else printf("(+) Restored character transmission and placed the transmission line in a nonbreak state\n"); // далее подготовим структуру DCB для того чтобы внести некоторые изменения в параметрах порта: DCB dcb; retcode = GetCommState(hCom,&dcb); if( retcode==NULL ) printf("(-) GetCommState() failed\n"); else printf("(+) Retrieved the current control settings for %s\n", port_ptr); // изменяем только то что нам нужно: dcb.BaudRate = CBR_9600; // set the baud rate dcb.ByteSize = 8; // data size, xmit, and rcv dcb.Parity = NOPARITY; // no parity bit dcb.StopBits = ONESTOPBIT; // one stop bit // и устанавливаем новый опции порта: retcode = SetCommState(hCom, &dcb); // и опять проверяем код возврата, на всякий случай... надеюсь я вас научу это всегда делать, // это является правилом хорошего тона. с таким подходом никогда не затеряетесь в своем коде // и легконайдете причину возникновения ошибок, если таковые будут if( retcode==NULL ) printf("(-) SetCommState() failed\n"); else printf("(+) Reinitialized all hardware and control settings \n"\ // устанавливаем события, о появлении которых мы хотим узнать: // (будем мониторить все события) DWORD CommEventMask = EV_BREAK | EV_CTS | EV_DSR | EV_ERR | EV_RING | EV_RLSD | EV_RXCHAR | EV_RXFLAG | EV_TXEMPTY; retcode = SetCommMask(hCom,CommEventMask); if( retcode==NULL ){ printf("(-) SetCommMask() failed\n"); PrintLastError(); }else printf("(+) Events to be monitored -> BREAK, CTS, DSR, ERR, RING, RLSD, RXCHAR, RXFLAG, TXEMPTY\n"); // теперь создадим event, который нам сообшит о проишедшем событии: OVERLAPPED OL; // эта структура требуется в многих функциях при асинхронном вв/в // как не сложно заметить, она содержит в себе параметр типа HANDLE, который и будет нашим // event'ом OL.hEvent=CreateEvent(NULL, TRUE, FALSE, NULL); DWORD EventMask = 0; //сюда будет записан тип события do{ // обрабатываем события в цикле, дабы программа не закончилась после обработки одного события // если этого непосредственно не нужно retcode = WaitCommEvent(hCom, &EventMask, &OL); if ( ( !retcode ) && (GetLastError()==ERROR_IO_PENDING) ){ printf("(!) Waiting for event\n"); WaitForSingleObject(OL.hEvent, INFINITE); } // поясняю этот код: // WaitCommEvent() ожидает событий от com-порта, тип событий ранее мы "привязали" к хэндлу порта // с помощью SetCommEvent() // retcode сообщает о результате выполнения функции. retcode должен быть NULL и // GetLastError() должна возвратать нам 997 (ERROR_IO_PENDING), что означает // "Overlapped I/O operation is in progress", тоесть функция успешно выполнилась и перевела // операцию ожидания событий в фоновый процесс, о чем мы и говорили раньше - при асинхронном // вв/в операции с портом не блокируют поток, в котором они выполняются. // далее при помощи WaitForSingleObject() ожидаем события, INFINITE - это константа, которая // задает интервал времени для ожидания в милисекундах, в даном случае INFINITE привыкли // считать как вечное ожидание, на самом деле это не так, это максимальное 32-х разрядное // число 0xFFFFFFFF, если подсчитать получается интервал ожидания больше чем год, думаю вам этого хватит 8)) // тоесть задав вместо INFINITE 1000 вы указываете ждать 1сек // далее анализируем чтоже произошло: if(EventMask & EV_BREAK) printf("(i) EV_BREAK\n"); if(EventMask & EV_RLSD) printf("(i) EV_RLSD\n"); if(EventMask & EV_CTS) printf("(i) EV_CTS\n"); if(EventMask & EV_DSR) printf("(i) EV_DSR\n"); if(EventMask & EV_ERR) printf("(i) EV_ERR\n"); if(EventMask & EV_RING) printf("(i) EV_RING\n"); if(EventMask & EV_RXCHAR) printf("(i) EV_RXCHAR\n"); if(EventMask & EV_RXFLAG) printf("(i) EV_RXFLAG\n"); if(EventMask & EV_TXEMPTY) printf("(i) EV_TXEMPTY\n"); // надеюсь тут все понятно, поразрядным & мы проверяем что у нас в EventMask // ну и далее в зависимости от события вы работаете с портом дальше, рассмотрим случай с // чтением данных из порта. в таком случае должно возникнуть событие EV_RXCHAR // итак, если у нас возникает EV_RXCHAR - в буфере порта нас ожидает сюрприз, а именно // какие-то данные. как же их поглядеть, а? 8) как как - считать в память и вывести на stdout 8) // для начала определим размер входящих данных. для этого нужно поглядеть на член структуры // COMSTAT - cbInQue // выше я писал, что для загрузки значений в COMSTAT пользуется функция ClearCommError() DWORD ErrorMask = 0; // сюда будет занесен код ошибки порта, если таковая была COMSTAT CStat; ClearCommError(hCom, &ErrorMask, &CStat); DWORD quelen = CStat.cbInQue; // все. тепереь quelen содержит количество байт в буфере порта. // выделяем память под буфер char *lpInBuffer = new char[ (int)quelen+1 ]; memset(&lpInBuffer, '\0', (int)quelen); DWORD dwReaded = 0; // эта переменная после вызова ReadFile будет содержать количество число, которое покажет // сколько байт было реально прочитано. как вы уже догадались, анализируя это значение можно // судить об успешности выполнения операции чтения. если у нас в буфере было 512байт, а // прочитали 0 или того меньше - чето не так, GetLastError() вам подскажет то именно 8)) retcode = ReadFile(com->hCom, lpInBuffer, quelen, &dwReaded, &OL); // опять же передаем структуру OL if( dwReaded == 0 && GetLastError() == ERROR_IO_PENDING ) { // если ничего не прочитано и GetLastError() возвратила ERROR_IO_PENDING // операция не успела за установленный в COMMTIMEOUTS // период времени выполниться и выполняется в фоне, тоесть вы можете спокойно чето делать // в этом болке кода а после проанализировать результат выполнения // DoSomeThing() //че-то делаем... а потом проверяем состояние retcode = GetOverlappedResult(hCom, &OL, &dwreaded, FALSE) ; // как вы уже догадались - dwreaded содержит кол-во прочитаных байт }else{ //если что-то прочитали или возникла ошибка if (dwReaded>0) //если прочитали данные printf("(+) %d bytes received (%s)\n", dwReaded, lpInBuffer); // по выводу вы увидете сколько байт прочитали и что именно else printf("(-) ReadFile() failed. errno %d\n", GetLastError()); } // теперь нужно сбросить значения буферов порта и регистров: if( PurgeComm(com->hCom, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR) == NULL ){ printf("(-) PurgeComm() failed\n"); PrintLastError(); }else printf("(+) Discarded all characters from the output and input buffer\n"); EventMask=0; ResetEvent(OL.hEvent); // сбрасываем значения нашего event'а перед ожиданием следующего события // } }while(1); выше могли заметить некую функцию PrintLastError(), это моя функция для вывода сообщения о последней ошибке, вот вам в качестве бонуса 8) void PrintLastError(void){ int ercode = GetLastError(); printf("(-) GetLastError() return %i ", ercode ); switch( ercode ){ case 2: printf("(The system cannot find the file specified)\n"); break; case 5: printf("(Access is denied)\n"); break; case 29: printf("(The system cannot write to the specified device)\n"); break; case 87: printf("(The parameter is incorrect)\n"); break; case 998: printf("(Invalid access to memory location)\n"); break; // тут описания для самых распространенных ошибок, остальные пару сотен добавите сами 8) default: printf("\n"); } } ну а как писать в порт? вместо ReadFile() пользуйте WriteFile() напоследок советую всем обзавестить MSDN, где все оч оч подробно описано 8) главное, что необходимо запомнить - всегда проверяйте коды возврата и если вы работаете в асинхронном режиме с портом - проверяйте на наличие ERROR_IO_PENDING, что будет свидетельствовать о том что операция выполняется в фоне и не стоит преждевременно суетиться и искать ошибки, которых нет 8) ps если у кого возникнут вопросы - пишите мне на bcodebox@gmail.com // // all rights (c) 2003 black c0de //tN [ The N0b0D1eS ] :: [ www.nteam.ru ] // /////////////////////////////////////////////////////////////////////////////////////// www.ssz.by.ru ;EOF