Модемы и факс-модемы. Программирование для MS-DOS и Windows.© Александр Фролов, Григорий ФроловТом 16, М.: Диалог-МИФИ, 1993. 7.2. Телекоммуникационные функцииПрактически все функции, которые мы будем описывать в разделе "Телекоммуникационные функции", могут быть использованы для работы с параллельным адаптером. Однако из-за ограниченного объема книги мы не приводим сведений об использовании параллельных портов компьютера. 7.2.1. Функция OpenCommПеред тем как приложение сможет начать работу с портом асинхронного последовательного адаптера, оно должно открыть этот порт при помощи функции OpenComm. int OpenComm(LPCSTR lpszDevControl, UINT cbInQueue, UINT cbOutQueue); Первый параметр функции lpszDevControl определяет открываемый порт и является указателем на строку, закрытую двоичным нулем, содержащую имя порта. Строка должна иметь формат "COMn" для асинхронного последовательного адаптера или "LPTn" для параллельного адаптера. Вместо символа n следует указать номер открываемого порта. Как вы уже знаете из предыдущих разделов, COM-порт работает под управлением специального драйвера асинхронного последовательного адаптера. Драйвер принимает данные из порта и записывает их во входную очередь. Затем приложение, по мере надобности может прочитать данные из входной очереди. Когда приложение передает данные в COM-порт, они сначала попадают в выходную очередь драйвера, после чего драйвер передает их непосредственно асинхронному адаптеру. При вызове функции OpenComm вы должны сами определить размер входной и выходной очереди драйвера асинхронного последовательного адаптера. Параметр cbInQueue задает размер входной очереди COM-порта в байтах, а параметр cbOutQueue - размер выходной очереди в байтах. В случае успешного выполнения функция возвращает число, определяющее открытый COM-порт. В дальнейшем оно будет использоваться практически всеми телекоммуникационными функциями. Мы будем называть его идентификатором COM-порта. Если по какой-либо причине COM-порт не открыт, функция OpenComm возвращает отрицательное значение - код ошибки. В следующей таблице перечислены возможные значения, возвращаемые функцией в случае возникновения ошибки:
Функцию OpenComm можно использовать для того, чтобы узнать открыт ли данный COM-порт. Если перед вызовом функции OpenComm присвоить параметрам cbInQueue и cbOutQueue нулевые значения, то функция возвращает константу IE_OPEN, в случае, когда порт уже открыт или IE_MEMORY в противном случае. В операционных системах Windows 3.1 и Windows for Worksgroups 3.11 можно использовать COM-порты с номерами от 1 до 9 (COM1-COM9) и параллельные порты от 1 до 3 (LPT1-LPT3). Если вы укажете номер порта, не поддерживаемый драйвером, функция OpenComm вернет код ошибки. Сразу после открытия порта для него устанавливаются режим, принятый по умолчанию (скорость передачи информации, формат данных и т. д.). Чтобы изменить этот режим, необходимо воспользоваться функцией SetCommState. Ниже мы приводим исходный текст функции OpenComPort, который вы можете использовать для открывания COM-порта. Чтобы открыть порт, достаточно передать этой функции номер порта. //========================================================== // Функция OpenCommPort //========================================================== int OpenCommPort(int nNumPort) { // Временный буфер для создания имени порта char szTmpNamePort[10]; wsprintf(szTmpNamePort, "COM%d", nNumPort); // Открываем COM-порт return OpenComm(szTmpNamePort, 8192, 8192); } Функция OpenCommPort самостоятельно формирует строку с текстовым именем открываемого COM-порта. Строка формируется с помощью функции wsprintf во временном буфере szTmpNamePort. Размер входной и выходной очереди COM-порта задается равным 8192 байтам, чего вполне достаточно для простых приложений, не поддерживающих протоколы обмена файлами. Затем определенная нами функция возвращает идентификатор открытого порта или отрицательное число в случае ошибки. 7.2.2. Функция CloseCommCOM-порт является аппаратным ресурсом компьютера. Когда приложение открывает COM-порт, он становится недоступен для других приложений. Поэтому после использования порта, его следует освободить. Для этой цели нужно воспользоваться функцией CloseComm. Функция CloseComm имеет следующий прототип: int CloseComm(int idComDev); Единственный параметр idComDev должен содержать идентификатор порта асинхронного последовательного адаптера, который будет закрыт. Идентификатор порта асинхронного последовательного адаптера возвращает описанная выше функция OpenComm. Функция возвращает ноль в случае успешного завершения или число, меньшее нуля, в случае ошибки. Одной из причин возникновения ошибки может быть неправильное значение параметра idComDev (т. е. если вы пытаетесь закрыть порт, который не был открыт. Функция CloseComm закрывает определенный порт асинхронного последовательного адаптера и освобождает всю оперативную память, выделенную для входной и выходной очереди данных. Перед тем как порт будет закрыт, все символы из выходной очереди будут переданы в COM-порт. Чтобы закрыть порт, не дожидаясь передачи всех оставшихся в выходной очереди символов, можно воспользоваться функцией FlushComm, сбрасывающей содержимое очередей без передачи. FlushComm(nPortID,0); // Сбросить входную очередь FlushComm(nPortID,1); // Закрыть COM-порт имеющий идентификатор nPortID CloseComm(nPortID); 7.2.3. Первая программаТеперь, когда мы рассмотрели первые две функции, предназначенные для управления портами асинхронного последовательного адаптера, приведем исходный текст приложения OPENCOMM, использующего эти функции. Приложение OPENCOMM последовательно открывает и закрывает все порты асинхронного адаптера, начиная с COM1 до COM8. При этом оно определяет номера доступных портов и выводит на экран их список. Главный файл приложения OPENCOMM представлен в листинге 7.1. Листинг 7.1. Файл OPENCOMM.CPP // ============================================================ // Определение доступных COM-портов // ============================================================ #define STRICT #include После запуска приложения OPENCOMM, функция WinMain вызывает функцию FindCOMPorts, определяющую список доступных COM-портов. Функция FindCOMPorts содержит цикл, в котором открывается очередной COM-порт: idComDev = OpenComm(szCommPattern, 1024, 1024); Если OpenComm возвращает значение большее или равное нулю, значит COM-порт успешно открыт. В этом случае мы устанавливаем в байте bFindPort бит с номером, соответствующим номеру открытого порта. После этого мы закрываем только что открытый порт: CloseComm(idComDev); Затем мы переходим к проверке следующего порта. Если функция OpenComm возвращает значение, меньшее нуля, мы считаем, что порт недоступен. Проверив в цикле COM-порты от COM1 до COM8, функция FindCOMPorts возвращает байт bFindPort. Каждый бит этого байта отвечает за свой COM-порт.
Получив байт bFindPort, WinMain формирует в строке szMsg список доступных портов и выводит его на экран с помощью функции MessageBox (см. рис. 7.1).
Рис 7.1. Список доступных COM-портов Файл определения модуля приложения OPENCOMM приведен в листинге 7.2. Листинг 7.2. Файл OPENCOMM.DEF ; ============================================================= ; Файл определения модуля ; ============================================================= NAME OPENCOMM DESCRIPTION 'Приложение OPENCOMM, (C) 1994, Frolov G.V.' EXETYPE windows STUB 'winstub.exe' STACKSIZE 5120 HEAPSIZE 1024 CODE preload moveable discardable DATA preload moveable multiple 7.2.4. Определение доступных портов Драйвер асинхронного последовательного адаптера VCD предоставляет приложениям Windows программный интерфейс через точку входа, адрес которой можно определить, вызвав мультиплексное прерывание INT 2Fh. Реализация этих функций зависит от конкретного драйвера VCD. Если у вас установлен драйвер, не совместимый с драйвером VCD из DDK, то эти функции могут не работать. В листинге 7.3. мы приводим исходный текст приложения FINDPORT, которое определяет порты асинхронного последовательного адаптера, доступные приложениям Windows, посредством вызова функции драйвера VCD. Листинг 7.3. Файл FINDPORT.CPP #define STRICT #include После запуска приложения, WinMain вызывает функцию GetWinFlags для определения режима работы Windows. Если Windows работает в стандартном режиме, приложение выводит соответствующее сообщение и завершает свою работу. Если Windows работает в расширенном режиме, выполняется вызов функции FindCOMPorts, определяющей список доступных COM-портов. Затем полученный список доступных портов отображается на экране. Функция FindCOMPorts возвращает байт, каждый бит которого отвечает за свой COM-порт. Младший бит соответствует порту COM1, а старший COM8. Исходный текст функции FindCOMPorts представлен в листинге 7.4. Листинг 7.4. Файл COMM_DRV.CPP #include В исходном тексте функции FindCOMPorts нет ни вызова функции OpenComm, ни вызовов других телекоммуникационных функций, она содержит только десяток строк на языке ассемблера. Рассмотрим функцию FindCOMPorts подробней. Сначала вызывается функция 1684h мультиплексного прерывания INT 2Fh. Для нее в регистре BX передается константа VCD_Device_ID, означающая, что необходимо получить точку входа для драйвера VCD. mov ax, 1684h mov bx, VCD_Device_ID xor di, di mov es, di int 2Fh Если после вызова мультиплексного прерывания в регистрах ES:DI сохраняются нулевые значения, значит драйвер VCD не поддерживает программный интерфейс и, следовательно, мы не можем получить к нему доступ. В этом случае возвращаем нулевое значение. Если содержимое регистров ES:DI не равно нулю, значит драйвер VCD доступен. Сохраняем адрес точки входа его интерфейса в переменной lpCOMM: mov word ptr lpCOMM, di mov word ptr lpCOMM+2, es Затем вызываем функцию из драйвера VCD, предварительно записав в регистр DX значение 1. mov dx, 1 call dword ptr lpCOMM После возвращения из этой функции по содержимому регистра AL можно определить, какие из портов асинхронного последовательного адаптера доступны для использования. Затем мы завершаем выполнение функции FindCOMPorts и возвращаем значение регистра AL. Файл определения модуля приложения FINDPORT приведен в листинге 7.5. Листинг 7.5. Файл FINDPORT.DEF ; ============================================================= ; Файл определения модуля ; ============================================================= NAME OPENCOMM DESCRIPTION 'Приложение FINDPORT, (C) 1994, Frolov G.V.' EXETYPE windows STUB 'winstub.exe' STACKSIZE 5120 HEAPSIZE 1024 CODE preload moveable discardable DATA preload moveable multiple 7.2.5. Функция SetCommStateИтак, мы научились открывать и закрывать COM-порт. Однако при вызове функции OpenComm мы указали только его имя и размеры входной и выходной очереди. Ни одной характеристики COM-порта - скорости передачи или формата данных мы не указывали. Дело в том, что функция OpenComm устанавливает для открываемого порта скорость обмена, формат данных и другие характеристики, принятые по умолчанию. Чтобы изменить эти характеристики, необходимо воспользоваться функцией SetCommState. Функция SetCommState устанавливает новый режим COM-порта, задаваемый полями структуры DCB, передаваемой ей в качестве одного из параметров. int SetCommState(const DCB FAR* lpdcb); Параметр lpdcb является дальним указателем на структуру DCB, содержащую описание устанавливаемого режима COM-порта. Подробное описание полей этой структуры мы привели чуть ниже. Если функция SetCommState успешно установила новый режим работы COM-порта, она возвращает нулевое значение. В противном случае функция возвращает отрицательное значение. Функция проводит повторную инициализацию всего аппаратного обеспечения COM-порта, но не сбрасывает входную и выходную очереди. Поэтому символы, расположенные во входной и выходной очередях, не будут удалены. В том случае, если желательно удалить эти символы и очистить очереди, следует воспользоваться функцией FlushComm. 7.2.6. Структура DCBПерейдем к подробному описанию структуры DCB (Device Control Block). Структура DCB содержит информацию, определяющую различные характеристики портов последовательного асинхронного адаптера. В структуре DCB определяется скорость передачи данных, количество бит данных и стоповых бит в передаваемых символах, устанавливается контроль четности и режим управления потоком. Структура DCB определена в файле WINDOWS.H следующим образом: typedef struct tagDCB { BYTE Id; UINT BaudRate; BYTE ByteSize; BYTE Parity; BYTE StopBits; UINT RlsTimeout; UINT CtsTimeout; UINT DsrTimeout; UINT fBinary :1; UINT fRtsDisable :1; UINT fParity :1; UINT fOutxCtsFlow :1; UINT fOutxDsrFlow :1; UINT fDummy :2; UINT fDtrDisable :1; UINT fOutX :1; UINT fInX :1; UINT fPeChar :1; UINT fNull :1; UINT fChEvt :1; UINT fDtrflow :1; UINT fRtsflow :1; UINT fDummy2 :1; char XonChar; char XoffChar; UINT XonLim; UINT XoffLim; char PeChar; char EofChar; char EvtChar; UINT TxDelay; } DCB; В файле WINDOWS.H определен также тип LPDCB - дальний указатель на структуру типа DCB: typedef DCB FAR* LPDCB; Опишем назначение отдельных полей структуры DCB. Поле IdСодержит идентификатор COM-порта. Значение поля устанавливается функцией BuildCommDCB, описанной ниже, и содержит то же самое значение, которое возвращает функция OpenComm при открытии этого порта. Если старший бит поля Id установлен, данная структура DCB описывает порт параллельного адаптера. Поле BaudRate Поле BaudRate определяет скорость передачи, с которой работает COM-порт. Скорость можно задать двумя способами. Вы можете записать в это поле либо одну из констант CBR_, определенных в файле WINDOWS.H, либо абсолютное значение скорости передачи информации. Список констант CBR_ и соответствующие им значения скорости перечислены в представленной ниже таблице.
Как видите, старший байт для констант CBR_ содержит значение 0xFF. Поэтому, если вы желаете записать в поле BaudRate абсолютное значение скорости, указывайте числа, меньшие, чем значение константы CBR_110 (0xFF10), и большие 2. Если вы запишите в поле BaudRate неправильное значение, функция SetCommState завершится с ошибкой IE_BAUDRATE. Поле ByteSizeВ поле ByteSize определяется количество бит в символах, передаваемых и принимаемых через COM-порт. Это поле может содержать любое значение от 4 до 8. Если вы запишете в него другое значение, то функция SetCommState завершится с ошибкой IE_BYTESIZE. Поле ParityПоле Parity управляет контролем четности и может содержать одну из пяти констант, перечисленных ниже:
Если записать в это поле другое значение, функция SetCommState вернет код ошибки IE_DEFAULT. Поле StopBitsПоле StopBits задает количество стоповых бит. Поле может содержать одну из констант, перечисленных ниже:
Если записать в это поле другое значение, то функция SetCommState вернет код ошибки IE_DEFAULT. Поле RlsTimeoutПоле RlsTimeout определяет максимальный интервал времени (тайм-аут), в течение которого функция WriteComm будет ожидать появления сигнала RLSD (ранее обозначался нами как DCD) перед тем, как записать данные в очередь передатчика. Интервал времени задается в миллисекундах. Вы можете записать в эти поля константы INFINITE или IGNORE, определенные в файле WINDOWS.H как 0xFFFF и 0x0, соответственно. INFINITE можно перевести как бесконечный, однако это не означает, что функция WriteComm будет бесконечно долго ждать сигнал RLSD. На самом деле эта константа задает интервал времени примерно 65 секунд. IGNORE означает, что WriteComm не ожидает сигнал RLSD. Если интервал ожидания истечет, а сигнал RLSD так и не установится, функция WriteComm вернет нулевое значение. Чтобы узнать причину ошибки, вызовите функцию GetCommError. Если функция WriteComm возвратила управление, после того как истек тайм-аут, GetCommError возвратит значение CE_RLSDTO. Поле CtsTimeoutПоле CtsTimeout определяет максимальный интервал времени (тайм-аут), в течение которого функция WriteComm будет ожидать появления сигнала CTS перед тем, как записать данные в очередь передатчика. Интервал времени задается в миллисекундах. Вы можете записать в эти поля константы INFINITE или IGNORE. Назначение этих констант уже было рассмотрено нами при описании поля RlsTimeout. Если интервал ожидания истечет, а сигнал CTS не установится, функция WriteComm вернет нулевое значение. Чтобы узнать причину ошибки, вызовите функцию GetCommError. Если функция WriteComm возвратила управление, после того как истек тайм-аут, GetCommError возвратит значение CE_CTSTO. Поле DsrTimeoutПоле DsrTimeout определяет максимальный интервал времени (тайм-аут), в течение которого функция WriteComm будет ожидать появления сигнала DSR перед тем, как записать данные в очередь передатчика. Интервал времени задается в миллисекундах. Вы можете записать в эти поля константы INFINITE или IGNORE. Назначение этих констант было уже рассмотрено при описании поля RlsTimeout. Если интервал ожидания истечет, а сигнал DSR не установится, функция WriteComm вернет нулевое значение. Чтобы узнать причину ошибки, вызовите функцию GetCommError. Если функция WriteComm возвратила управление, после того как истек тайм-аут, GetCommError возвратит значение CE_DSRTO. Поле fBinaryПоле fBinary, которое используется как флаг, устанавливает режим передачи двоичной информации. Если это поле содержит значение FALSE (ноль), принятый символ EOF распознается как сигнал для окончания приема данных. Символ EOF определяется полем EofChar структуры DCB. После того как функция ReadComm прочтет символ EOF из входной очереди, она возвращает управление, не прочитав остальные данные. Если во входном буфере остались непрочитанные данные, они распознаются как переполнение. В этом случае функция GetCommError вернет код ошибки CE_RXOVER. При получении символа EOF в структуре COMSTAT устанавливается флаг CSTF_EOF. Если в поле fBinary записано значение TRUE (единица), символ EOF обрабатывается так же, как и остальные символы. Это позволяет беспрепятственно передавать через COM-порт не только текстовые, но также и двоичные данные. Поле fRtsDisableПоле fRtsDisable определяет, будет ли использоваться сигнал RTS. Если это флаг установлен, сигнал RTS не используется и все время остается равен нулю. Если поле fRtsDisable содержит нулевое значение (FALSE), сигнал RTS устанавливается, когда COM-порт открывается и сбрасывается, когда порт закрывается. Чтобы использовать сигнал RTS для управления потоком, необходимо записать в поле fRtsDisable нулевое значение, а в поле fRtsFlow - единицу. Другие комбинации значений fRtsDisable и fRtsFlow блокируют управление потоком по линии RTS. Ниже перечислены различные комбинации полей fRtsDisable и fRtsFlow:
Если сигналы RTS и DTR не используются для управления потоком, их можно устанавливать и сбрасывать с помощью функции EscapeCommFuntion. Поле fParityПоле fParity определяет, будет ли выполняться проверка принимаемой и передаваемой информации на четность. Если в этом поле записано значение TRUE, проверка на четность выполняется. Если функция ReadComm обнаружит ошибку четности, она возвращает отрицательную величину. В этом случае вы должны воспользоваться функцией GetCommError, чтобы определить причину и сбросить флаг ошибки CE_RXPARITY. Чтобы при обнаружении ошибки по четности в функцию окна поступало сообщение EV_ERR, воспользуйтесь функцией SetCommEventMask. Поле fOutxCtsFlowПоле fOutxCtsFlow определяет, что сигнал CTS используется для управления выходным потоком данных. Если это поле содержит значение TRUE и сигнал CTS выключен, передача информации приостанавливается до тех пор, пока сигнал CTS не установится снова. Если поле CtsTimeout содержит величину, отличную от нуля, функция WriteComm будет ожидать появления сигнала CTS вне зависимости от значения поля fOutxCtsFlow. Поле fOutxDsrFlowПоле fOutxDsrFlow определяет, что сигнал DSR используется для управления выходным потоком. Если это поле содержит значение TRUE и сигнал DSR выключен, передача информации приостанавливается до тех пор, пока сигнал CTS снова не установится. Если поле DsrTimeout содержит величину, отличную от нуля, функция WriteComm будет ожидать появления сигнала DSR вне зависимости от значения поля fOutxDsrFlow. Поле fDummyПоле fDummy зарезервировано. Поле fDtrDisableПоле fDtrDisable определяет, будет ли использоваться сигнал DTR. Если это флаг установлен, сигнал DTR не используется и остается равен нулю. Если поле fDtrDisable содержит нулевое значение (FALSE), сигнал DTR устанавливается, когда COM-порт открыт, и сбрасывается, когда порт закрывается. Обычно поле fDtrDisable используют совместно с fDtrFlow. Их использование аналогично полям fRtsDisable и fRtsFlow. Поле fOutXПоле fOutX включает протокол управления потоком XON/XOFF. Если это поле установлено, передача информации приостанавливается, когда принимается символ XoffChar, и возобновляется при получении сигнала XonChar. Если передача данных прерывается в результате приема символа XOFF, в структуре COMSTAT устанавливается флаг CSTF_XOFFHOLD. Поле fInXПоле fInX устанавливает протокол управления потоком XON/XOFF при приеме данных. Если это поле установлено, то символ XoffChar передается, когда в очереди приемника становится больше, чем XoffLim символов и символ XonChar, когда в очереди приемника становится меньше, чем XonLim символов. Если передача данных прерывается в результате передачи символа XOFF, в структуре COMSTAT устанавливается флаг CSTF_XOFFSENT. Поле fPeCharЕсли поле fPeChar содержит значение TRUE, каждый символ, полученный с ошибкой по четности, будет заменен на символ, заданный полем PeChar. Поле fNullЕсли поле fNull содержит значение TRUE, все принятые из COM-порта символы, имеющие нулевое значение, будут пропускаться. Поле fChEvtПри установке поля fChEvt, получение символа EvtChar отмечается как событие EV_RXFLAG. В действительности поле fChEvt не используется. Необходимо вызвать функцию SetCommEventMask, чтобы разрешить это событие. Поле fDtrflowПоле fDtrflow определяет, что сигнал DTR используется для протокола управления потоком (при приеме данных). Если этот флаг установлен, сигнал DTR выключается, когда в приемной очереди становится больше чем XoffLim символов, и включается снова, когда в приемной очереди становится меньше, чем XonLim символов. Такой механизм позволяет предотвратить переполнение входной очереди COM-порта, которое может вызвать потерю принятых данных. Поле fRtsflowПоле fRtsflow определяет, что сигнал RTS используется для управления потоком (при приеме данных). Если это поле установлено, сигнал RTS выключается, когда в приемной очереди становится больше чем XoffLim символов и включается, когда в приемной очереди становится меньше, чем XonLim символов. Поле fDummy2Поле fDummy2 зарезервировано. Поле XonCharПоле XonChar содержит значение символа XON для передачи и для приема. Смотри описание полей fInX и fOutX. Функция BuildCommDCB записывает в это поле значение 0x11 ( Поле XoffCharПоле XoffChar определяет значение символа XOFF для передачи и для приема. Смотри описание полей fInX и fOutX. Функция BuildCommDCB, описанная ниже, записывает в это поле значение 0x13 ( Если вы решили изменить принятые по умолчанию значения полей XonChar и XoffChar, следует обратить внимание на то, что они должны содержать различные величины. В противном случае управление потоком при помощи символов XON/XOFF будет работать неправильно и обмен данными может прекратиться. Поле XonLimПоле XonLim определяет минимально допустимое число символов в приемной очереди, при принижении которого передается символ XON (если включен протокол XON/XOFF) и устанавливается сигнал DTR (если включен протокол DTR). Функция BuildCommDCB записывает в это поле значение 0x10. Поле XoffLimПоле XoffLim определяет максимально допустимое число символов в приемной очереди, при превышении которого передается символ XOFF (если включен протокол) и сбрасывается сигнал DTR (если включен протокол). Чтобы определить максимально допустимое количество символов в очереди, надо вычесть из размера очереди приемника значение поля XoffLim. Функция BuildCommDCB записывает в это поле значение 0x10. Для изменения значения, записанного в поле, можно воспользоваться функцией SetCommState. Поле PeCharПоле PeChar задает значение символа, используемого для замещения символов, принятых с ошибкой по четности. Функция BuildCommDCB записывает в это поле значение 0x0. Смотри поле fPeChar и fParity. Поле EofCharПоле EofChar определяет символ EOF, используемый при передаче сигнала "конец данных". Функция BuildCommDCB записывает в это поле значение 0x0. Смотри поле fBinary. Поле EvtCharПоле EvtChar определяет символ, используемый для передачи сигнала событие (EV_RXFLAG). Когда принимается символ EvtChar, генерируется событие EV_RXFLAG. В действительности флаг fChEvt не используется. Необходимо вызвать функцию SetCommEventMask, чтобы разрешить это событие. Функция BuildCommDCB записывает в это поле значение 0x0. Поле TxDelayПоле TxDelay не используется в Windows 3.1 и Windows for Workgroups 3.11. Как заполнить или модифицировать структуру DCBНа этом мы заканчиваем описание структуры DCB. В заключение приведем несколько советов по модификации полей этой структуры. Структура DCB заполняется двумя функциями - OpenComm и BuildCommDCB. Однако существует много полей в структуре DCB, которые нельзя изменить с помощью этих функций. Для этого надо непосредственно записать новые значения в поля структуры, а затем воспользоваться функцией SetCommState, чтобы внесенные изменения отразились на работе COM-порта. Если вам нужно изменить какое-либо поле этой структуры, следует сначала узнать текущее значение полей структуры DCB, с помощью функции GetCommState. Затем внесите все необходимые изменения в поля структуры и вызовите функцию SetCommState, чтобы соответственно изменился режим COM-порта. Такой алгоритм гарантирует, что остальные поля структуры DCB перед вызовом SetCommState будут содержать правильные значения. В качестве альтернативного способа изменения структуры DCB можно воспользоваться функцией BuildCommDCB. Этой функции передается в качестве параметра строка, имеющая формат команды MODE операционной системы MS-DOS. Данная строка может содержать значения скорости передачи данных, количества бит данных и стоповых бит в слове. Функция BuildCommDCB заполняет структуру DCB, но не изменяет состояния COM-порта. Для этого следует воспользоваться функцией SetCommState. В начале работы с COM-портом приложение должно открыть его с помощью функции OpenComm. Функция OpenComm с помощью функции BuildCommDCB заполняет временный, недоступный программисту, блок DCB значениями по умолчанию. Скорость обмена устанавливается равной 9600 бит/с., проводится проверка на четность, передаваемые символы содержат 7 информационных и один стоповый бит. Затем функция OpenComm выполняет внутренний вызов функции SetCommState с подготовленной структурой DCB, устанавливая начальный режим COM-порта. Затем можно создать собственную структуру DCB и установить новый режим COM-порта, вызвав функцию SetCommState. Функция SetCommState принимает в качестве параметра указатель на заполненную структуру DCB и устанавливает режим работы COM-порта в соответствии с данными из этой структуры. Вы также можете узнать текущее состояние COM-порта с помощью функции GetCommState. 7.2.7. Функция BuildCommDCBФункция BuildCommDCB заполняет структуру DCB в соответствии с переданной ей строкой параметров. Структура DCB содержит управляющую информацию, необходимую для установки режима работы портов последовательного асинхронного адаптера (см. раздел "Структура DCB"). int BuildCommDCB(LPCSTR lpszDef, DCB FAR* lpdcb); Параметр lpszDef является дальним указателем на строку символов, закрытую двоичным нулем, которая должна содержать команды установки режима COM-порта. Формат этой строки полностью соответствует параметрам команды MODE операционной системы MS-DOS. Например, параметр lpszDef может указывать на строку "COM1:9600,n,8,1". В этом случае после вызова функции BuildCommDCB заполненная структура DCB позволит установить порт COM1 в режим обмена данными со скоростью 9600 бит/с, без проверки на четность с форматом передаваемых символов из 8 бит данных и одним стоповым битом. Параметр lpdcb является дальним указателем на структуру типа DCB. В нее будет записана информация, полученная после интерпретации строки lpszDef. Функция BuildCommDCB возвращает ноль, если команды, указанные в параметре lpszDef, успешно интерпретированы и структура DCB заполнена. В случае возникновения ошибки, например, в формате команд, функция возвращает -1. Функция BuildCommDCB только заполняет поля структуры DCB. Чтобы установить режим порта последовательного асинхронного адаптера в соответствии с заполненной структурой DCB, необходимо воспользоваться функцией SetCommState. С помощью функции BuildCommDCB вы можете управлять далеко не всеми характеристиками COM-порта, определяемыми структурой DCB. Так как формат команд строки lpszDef соответствует команде MODE MS-DOS, то с помощью BuildCommDCB можно изменить только скорость передачи информации, режим проверки на четность и формат передаваемых данных (число стоповых бит и бит данных в передаваемых символах). Остальные поля структуры DCB функция BuildCommDCB заполняет по собственному усмотрению (см. описание полей структуры DCB в разделе "Структура DCB"). Так, BuildCommDCB запрещает управление потоком данных между модемом и компьютером на аппаратном уровне (сигналы CTS и RTS) и на программном уровне (с помощью символов XON и XOFF). Кроме того, функция BuildCommDCB позволяет определить только символы длиной 7 или 8 бит. Другие значения вызывают ошибку. Таким образом, функция BuildCommDCB может быть полезна только в самых простых случаях или для начального заполнения структуры DCB. В большинстве случаев вам потребуется самостоятельно заполнять поля этой структуры. 7.2.8. Функция SetCommBreakФункция SetCommBreak прекращает передачу данных и переводит COM-порт в состояние BREAK. COM-порт находится в состоянии BREAK до тех пор, пока приложение не вызовет функцию ClearCommBreak. int SetCommBreak(int idComDev); Параметр idComDev должен содержать идентификатор COM-порта, переводимого в состояние BREAK. В качестве параметра idComDev необходимо использовать величину, возвращаемую функцией OpenComm. При успешном завершении функция возвращает ноль. Если функция возвращает значение меньшее нуля, произошла ошибка. 7.2.9. Функция ClearCommBreakФункция ClearCommBreak восстанавливает обмен данными через порт асинхронного последовательного адаптера, прерванный ранее вызовом функции SetCommBreak. int ClearCommBreak(int idComDev); Единственный параметр функции idComDev должен содержать идентификатор порта асинхронного последовательного адаптера, для которого вызывается функция. Функция возвращает ноль в случае успешного завершения или -1, если параметр idComDev задан неправильно, то есть не соответствует ни одному открытому порту асинхронного последовательного адаптера. 7.2.10. Функция EnableCommNotificationФункция EnableCommNotification разрешает или запрещает передачу сообщения WM_COMMNOTIFY в функцию окна приложения. Более подробную информацию о сообщении WM_COMMNOTIFY можно получить, прочитав раздел "Сообщение WM_COMMNOTIFY". Прототип функции EnableCommNotification представлен ниже. BOOL EnableCommNotification(int idComDev, HWND hwnd, int cbWriteNotify, int cbOutQueue); Параметр idComDev является идентификатором порта, который будет передавать сообщения WM_COMMNOTIFY в функцию окна. Для параметра idComDev необходимо использовать значение, полученное от функции OpenComm. Параметр hwnd определяет окно, функции которого будет передаваться сообщение WM_COMMNOTIFY. Если этот параметр содержит значение NULL, функция EnableCommNotification запрещает передачу сообщения WM_COMMNOTIFY в функцию текущего окна. Параметр cbWriteNotify определяет количество байт, которое драйвер асинхронного последовательного адаптера должен записать во входную очередь перед тем, как будет передано сообщение WM_COMMNOTIFY, у которого в младшем слове параметра lParam записан код извещения CN_RECEIVE . Данное сообщение служит сигналом приложению, что пора прочитать данные из входной очереди COM-порта. Последний параметр cbOutQueue задает минимальное количество символов (байт) в очереди передатчика. Если в очереди передатчика становится меньше байт, чем определено параметром cbOutQueue, драйвер асинхронного последовательного адаптера передает приложению сообщение WM_COMMNOTIFY, у которого в младшем слове параметра lParam записан код извещения CN_TRANSMIT. Это сообщение означает, что выходная очередь практически пустая и можно поместить в нее очередную порцию данных. В случае успешного завершения, функция возвращает значение отличное от нуля. Если функция завершилась с ошибками, она возвращает ноль. Причиной возникновения ошибки может послужить вызов функции EnableCommNotification с неправильным идентификатором COM-порта (COM-порт не открыт) или то, что драйвер асинхронного последовательного адаптера не поддерживает функцию EnableCommNotification. Так, например, драйвер асинхронного последовательного адаптера COMM.DRV операционной системы Windows версии 3.0 не поддерживает эту функцию. Вы можете запретить передачу функции окна сообщения WM_COMMNOTIFY с кодами извещения CN_RECEIVE и CN_TRANSMIT. Если задать для параметра cbWriteNotify значение -1, драйвер не будет передавать функции окна сообщение WM_COMMNOTIFY с кодом извещения CN_RECEIVE. Аналогично, если задать для параметра cbOutQueue значение -1, драйвер не будет передавать функции окна сообщение WM_COMMNOTIFY с кодом извещения CN_TRANSMIT. Может возникнуть резонный вопрос: что произойдет, если разрешить генерацию сообщения WM_COMMNOTIFY с кодом извещения CN_RECEIVE, задать пороговое значение для передачи сообщения 20 байт, а из COM-порта поступит только 15 байт? Будет ли драйвер ожидать прихода остальных 5 байт и как долго? Не прекратится ли обмен данными? Драйвер асинхронного последовательного адаптера периодически, с интервалом около 100 миллисекунд, опрашивает все очереди COM-портов. Если при очередном опросе входной очереди в ней будет находится меньше чем cbWriteNotify байт, функции окна все равно передается сообщение WM_COMMNOTIFY с кодом извещения CN_RECEIVE. Таким образом, если во входной очереди находится меньше байт, чем надо для генерации сообщения, функция окна все равно получит сообщение WM_COMMNOTIFY по истечении 100 миллисекунд. 7.2.11. Функция FlushCommФункция FlushComm удаляет все символы из входной или выходной очереди COM-порта. Прототип функции имеет следующий вид: int FlushComm(int idComDev, int fnQueue); Параметр idComDev является идентификатором COM-порта, очередь которого необходимо очистить (сбросить). Параметр fnQueue определяет очередь, из которой будут удалены все символы. Если значение этого параметра равно нулю, очищается выходная очередь. Если же значение параметра равно единице, удаляются все символы из приемной очереди. Функция возвращает ноль в случае нормального завершения. Если значение, возвращаемое функцией, меньше нуля, параметр idComDev определяет неоткрытый COM-порт или параметр fnQueue задает несуществующую очередь. Возвращаемое функцией число принимает положительное значение, если возникла ошибка COM-порта. Подробный список возможных ошибок приведен в описании функции GetCommError. 7.2.12. Функция GetCommErrorФункция GetCommError позволяет определить текущее состояние COM-порта и причину ошибки при предыдущем вызове коммуникационной функции. Когда случается ошибка при передаче или приеме данных, Windows блокирует COM-порт до тех пор, пока не будет вызвана функция GetCommError. int GetCommError(int idComDev, COMSTAT FAR* lpStat); Параметр idComDev должен содержать идентификатор порта, который будет проверяться. Через параметр lpStat передается дальний указатель на структуру типа COMSTAT. В поля этой структуры будет записано состояние COM-порта. В случае если вы вызовете GetCommError с параметром lpStat, равным NULL, функция вернет только значение ошибки. Структура COMSTAT определена в файле WINDOWS.H следующим образом: #if (defined(STRICT) || (WINVER >= 0x030a)) // Основное определение структуры COMSTAT typedef struct tagCOMSTAT { BYTE status; UINT cbInQue; UINT cbOutQue; } COMSTAT; // Определение вспомогательных констант #define CSTF_CTSHOLD 0x01 #define CSTF_DSRHOLD 0x02 #define CSTF_RLSDHOLD 0x04 #define CSTF_XOFFHOLD 0x08 #define CSTF_XOFFSENT 0x10 #define CSTF_EOF 0x20 #define CSTF_TXIM 0x40 #else /* (STRICT || WINVER >= 0x030a) */ // Альтернативное определение структуры COMSTAT typedef struct tagCOMSTAT { BYTE fCtsHold :1; BYTE fDsrHold :1; BYTE fRlsdHold :1; BYTE fXoffHold :1; BYTE fXoffSent :1; BYTE fEof :1; BYTE fTxim :1; UINT cbInQue; UINT cbOutQue; } COMSTAT; #endif /* !(STRICT || WINVER >= 0x030a */ Из приведенного выше листинга видно, что структура COMSTAT определяется по-разному в зависимости от следующего условия: #if(defined(STRICT) || (WINVER >= 0x030a)) // Основное определение структуры COMSTAT // .... #else /* (STRICT || WINVER >= 0x030a) */ // Альтернативное определение структуры COMSTAT // .... #endif Если вы создаете загрузочный модуль для операционной системы Windows 3.1 или устанавливаете жесткий режим контроля синтаксиса программы, определив константу STRICT, используется основное определение структуры COMSTAT. Опишем поля структуры COMSTAT при использовании основного определения:
Функция GetCommError возвращает значение, являющееся комбинацией флагов. Флаги определяют ошибки, возникшие при последнем вызове коммуникационной функции. Возвращаемое значение может быть комбинацией из следующих флагов:
7.2.13. Функция SetCommEventMaskДля каждого COM-порта Windows поддерживает слово событий. Оно состоит из набора бит, которые устанавливаются при изменении состояния COM-порта (например, при изменении состояния сигнала CTS). Чтобы при изменении состояния COM-порта соответствующим образом менялось слово состояния, необходимо сначала разрешить регистрацию этих изменений. Функция SetCommEventMask разрешает регистрацию изменений состояния COM-порта в слове событий данного COM-порта: UINT FAR* SetCommEventMask(int idComDev, UINT fuEvtMask); Параметр idComDev является идентификатором COM-порта. Это значение возвращает функция OpenComm. Параметр fuEvtMask определяет, какие события будут разрешены. Этот параметр может быть комбинацией из любых констант, перечисленных в следующей таблице:
Функция SetCommEventMask возвращает указатель на слово событий данного COM-порта. Каждый бит этого слова отвечает за определенное событие. Если бит установлен, значит соответствующее событие произошло. В слово событий записываются только события, разрешенные функцией SetCommEventMask. Воспользовавшись функцией EnableCommNotification, можно разрешить передачу в функцию окна приложения сообщения WM_COMMNOTIFY с кодом извещения CN_EVENT. Это сообщение будет передаваться при изменении слова состояния COM-порта. 7.2.14. Функция GetCommEventMaskФункция GetCommEventMask получает и затем очищает слово события данного COM-порта. UINT GetCommEventMask(int idComDev, int fnEvtClear); Параметр idComDev является идентификатором COM-порта. Параметр fnEvtClear определяет события, которые будут сброшены в слове событий. Если функция завершается успешно, она возвращает значение слова событий для COM-порта, определенного параметром idComDev. Каждый бит этого слова отвечает за одно конкретное событие. Если данное событие произошло, соответствующий бит равен 1. Перед тем как функция GetCommEventMask может зарегистрировать возникновение события, приложение должно разрешить данное событие при помощи функции SetCommEventMask. При возникновении таких событий, как изменение состояния линии или ошибка принтера, вы должны вызвать функцию GetCommError. 7.2.15. Функция GetCommStateФункция GetCommState позволяет узнать режим работы порта асинхронного последовательного адаптера. Прототип функции представлен ниже: int GetCommState(int idComDev, DCB FAR* lpdcb); Параметр idComDev должен содержать идентификатор COM-порта. Это значение возвращает функция OpenComm. Параметр lpdcb является дальним указателем на структуру DCB, в которую будет записана информация о режиме работы COM-порта, указанного в параметре idComDev. В случае успешного завершения функция GetCommState возвращает нулевое значение. Если при работе функции возникла ошибка, возвращаемая величина меньше нуля. 7.2.16. Функция ReadCommФункция ReadComm позволяет прочитать данные из входной очереди COM-порта. Прототип функции представлен ниже: int ReadComm(int idComDev, void FAR* lpvBuf, int cbRead); Параметр idComDev является идентификатором COM-порта, из которого будут прочитаны данные. Параметр lpvBuf содержит дальний указатель на буфер, в который будут записаны прочитанные из COM-порта данные. Последний параметр cbRead задает количество символов, которое следует прочитать из входной очереди порта. Следите за тем, чтобы значение cbRead не было больше, чем размер буфера lpvBuf В случае успешного выполнения функции возвращаемое значение определяет количество символов, прочитанное из COM-порта. Если во время выполнения функции произошла ошибка, функция возвращает отрицательное значение. При этом абсолютное значение возвращенной величины определяет количество символов, успешно прочитанных из COM-порта. В случае возникновения ошибки ее причину можно выяснить с помощью функции GetCommError. Так как ошибка может произойти и в том случае, когда ни один символ из входной очереди не прочитан, то, если ReadComm возвращает нулевую величину, следует вызвать функцию GetCommError, чтобы удостоверится в отсутствии ошибок. Если величина, возвращаемая функцией меньше чем значение параметра cbRead, это означает, что на момент вызова функции во входной очереди COM-порта находилось символов меньше, чем определено параметром cbRead. Если величина, возвращаемая функцией равна значению параметра cbRead, то возможно, что в очереди приемника еще находятся данные. Поэтому можно сразу повторно вызвать функцию ReadComm. В качестве примера использования функции ReadComm мы приводим исходный текст функции ReadCommChar, считывающей из входной очереди один символ: //========================================================== // Функция ReadCommChar //========================================================== int ReadCommChar(int nPortID) { int iResult = 0; int iErr = 0; // Считываем из COM-порта один символ iErr = ReadComm(nPortID, (LPSTR)&iResult, 1); // Если символ не прочитан, обрабатываем ошибку if(iErr != 1) { iResult = -1; // Сбрасываем флаги ошибок GetCommError(nPortID, NULL); } return iResult; } В качестве параметра для функции ReadCommChar необходимо передать идентификатор COM-порта. Функция ReadCommChar вызывает ReadComm и пытается считать из входной очереди COM-порта один символ. Если ReadComm возвращает единицу, символ успешно прочитан и функция ReadCommChar возвращает код полученного символа. В противном случае для того, чтобы сбросить флаги ошибок, вызывается функция GetCommError. Затем функция ReadCommChar возвращает -1. 7.2.17. Функция TransmitCommCharФункция TransmitCommChar помещает символ, заданный параметром chTransmit, в начало выходной очереди COM-порта, определенного параметром idComDev. Прототип функции представлен ниже. int TransmitCommChar(int idComDev, char chTransmit); Функция возвращает нулевое значение в случае успешного завершения или отрицательное значение, если символ не записан в выходную очередь. Функцию нельзя вызывать повторно, если COM-порт не производит передачу данных. После того как функция TransmitCommChar записала символ в начало выходной очереди, необходимо, чтобы перед повторным вызовом функции этот символ был передан. Если предыдущий символ не был передан, перед повторным вызовом функции TransmitCommChar, функция завершается с ошибкой. В следующем примере функция TransmitCommChar используется для передачи символов, набранных на клавиатуре в COM-порт. case WM_CHAR: ch = (char)wParam; // Помещаем код нажатой клавиши в начало выходной очереди TransmitCommChar(idComDev, ch); // Добавляем символ перевода строки LF // для каждого символа возврата каретки if (ch == 0x0d) TransmitCommChar(idComDev, 0x0a); return TRUE; 7.2.18. Функция UngetCommCharФункция позволяет поместить символ в начало входной очереди. При выполнении последующей операции чтения функция ReadComm прочитает этот символ. Прототип функции UngetCommChar представлен ниже: int UngetCommChar(int idComDev, char chUnget); Первый параметр функции idComDev должен содержать идентификатор COM-порта. Параметр chUnget должен содержать символ, помещаемый во входную очередь COM-порта. Функция возвращает нулевое значение в случае успешного завершения или отрицательное значение при возникновении ошибки. Последовательные вызовы функции UngetCommChar не допускаются. Перед тем как вы сможете вызвать функцию UngetCommChar еще раз, символ, помещенный в очередь приемника, должен быть прочитан. 7.2.19. Функция WriteCommФункция WriteComm записывает символы в выходную очередь COM-порта. Прототип функции представлен ниже: int WriteComm( int idComDev, const void FAR* lpvBuf, int cbWrite); Параметр idComDev содержит идентификатор COM-порта, используемого для передачи данных. Параметр lpvBuf является дальним указателем на буфер, содержащий передаваемые данные. Последний параметр функции cbWrite определяет количество символов в буфере lpvBuf, предназначенных для передачи. Обратите внимание на то, что значение параметра cbWrite не должно быть больше, чем размер буфера lpvBuf В случае успешного выполнения функции возвращаемое ей значение определяет количество символов, записанное в выходную очередь COM-порта. Если во время выполнения функции произошла ошибка, функция возвращает отрицательное значение. Абсолютное значение возвращаемой величины определяет количество символов, успешно записанных в выходную очередь. В случае возникновения ошибки ее причину можно выяснить с помощью функции GetCommError. Если в очереди передатчика недостаточно места, функция WriteComm может удалить данные из этой очереди. Поэтому перед вызовом функции WriteComm приложение должно проверить наличие достаточного свободного пространства в очереди передатчика. Для этого следует воспользоваться функцией GetCommError. При открытии COM-порта функцией OpenComm следует указать размер выходной очереди не меньше, чем максимальный предполагаемый размер передаваемых строк. В качестве примера использования функции WriteComm мы приводим исходный текст функции WriteCommChar, записывающей в выходную очередь один символ: //========================================================== // Функция WriteCommChar //========================================================== BOOL WriteCommChar(int nPortID, int nChar) { int iErr = 0; iErr = WriteComm(nPortID, (LPSTR)&nChar, 1)) if(iErr != 1) { GetCommError(nPortID, NULL); return FALSE; } return TRUE; } В качестве первого параметра функции WriteCommChar передается идентификатор COM-порта, в который необходимо передать символ. Второй параметр должен содержать код передаваемого символа. Функция WriteCommChar вызывает функцию WriteComm и пытается записать в выходную очередь COM-порта один символ. Если WriteComm возвращает единицу, значит символ успешно записан и функция WriteCommChar возвращает значение TRUE. В противном случае для того чтобы сбросить флаги ошибок вызывается функция GetCommError и WriteCommChar возвращает FALSE. 7.2.20. Сообщение WM_COMMNOTIFYСообщение WM_COMMNOTIFY отправляется драйвером COM-порта в функцию окна и служит извещением об изменении состояния COM-порта. Такое сообщение может поступать в функцию окна при наполнении входной очереди до определенного порогового значения, изменении состояния сигналов DTR, RI, RTS и т. д. Параметр wParam сообщения WM_COMMNOTIFY определяет идентификатор COM-порта, вызвавшего отправку сообщения. Через младшее слово параметра lParam функция окна получает число, по которому можно судить о причине посылки сообщения WM_COMMNOTIFY. Это число может быть комбинацией следующих кодов извещения:
Если приложение обработало поступившее ему сообщение WM_COMMNOTIFY, оно должно возвратить нулевое значение. Сообщение WM_COMMNOTIFY с кодом извещения CN_EVENT передается только тогда, когда слово состояния событий изменяет свое значение. Приложение, которое принимает сообщение WM_COMMNOTIFY, должно каждый раз очищать слово состояния событий, для того чтобы гарантировать получение сообщений в дальнейшем. Использование функции EnableCommNotification для разрешения передачи сообщений WM_COMMNOTIFY с кодами извещения CN_RECEIVE и CN_TRANSMIT может вызвать генерацию ошибочных сообщений WM_COMMNOTIFY, для которых параметр NotifyStatus равен нулю. При обмене данными на высоких скоростях это может привести к аварийной ситуации и даже перезагрузке компьютера. Для решения этой проблемы можно использовать следующие методы: Событие CN_RECEIVE формируется в тех случаях, когда количество байт во входной очереди превышает пороговое значение cbWriteNotify, установленное функцией EnableCommNotification, или когда истекло время (тайм-аут). После формирования события CN_RECEIVE в случае превышения пороговой величины cbWriteNotify другие сообщения CN_RECEIVE не будут генерироваться до тех пор, пока количество байт во входной очереди не станет меньше значения cbWriteNotify и не превысит ее снова. Сообщение CN_TRANSMIT создается аналогично CN_RECEIVE. Порог устанавливается параметром cbOutQueue функции EnableCommNotify. Когда количество символов в выходной очереди становится меньше, чем cbOutQueue, формируется сообщение CN_TRANSMIT. Другие сообщения CN_TRANSMIT не будут посылаться до тех пор, пока в буфере не станет больше, чем cbOutQueue символов. Однако, если прерывания поступают достаточно быстро, дополнительные сообщения CN_RECEIVE (или CN_TRANSMIT) могут посылаться до того, как количество символов в выходной очереди станет больше, чем cbWriteNotify. Эти сообщения можно пропускать (игнорировать), однако они могут послужить причиной перезагрузки системы. Ниже приведен фрагмент обработчика сообщения WM_COMMNOTIFY: //========================================================== // Функция окна WndProc //========================================================== LRESULT CALLBACK _export WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { switch( message ) { case WM_COMMNOTIFY: { if(CN_EVENT & LOWORD( lParam ) == CN_EVENT) { GetCommEventMask(COMDEV( npTTYInfo ), EV_RXCHAR); return(TRUE); } else if(CN_RECEIVE & LOWORD( lParam ) == CN_RECEIVE) { return(TRUE); } else if(CN_TRANSMIT & LOWORD( lParam ) == CN_TRANSMIT) { return(TRUE); } else if(LOWORD( lParam ) == 0) return(TRUE); } default: return(DefWindowProc(hwnd, message, wParam, lParam)); } } При загрузке драйвера последовательного асинхронного адаптера он вызывает функцию CreateSystemTimer и создает таймер, посылающий сообщения драйверу каждые 100 миллисекунд. Обрабатывая сообщения таймера, драйвер просматривает состояние всех открытых COM-портов и проводит проверку тайм-аута. Период таймера изменить нельзя. |