Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых

Модемы и факс-модемы. Программирование для 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 возвращает отрицательное значение - код ошибки. В следующей таблице перечислены возможные значения, возвращаемые функцией в случае возникновения ошибки:

ЗначениеСмысл
IE_BADIDНеверный или неподдерживаемый идентификатор COM-порта
IE_BAUDRATEУстановлена скорость передачи информации не поддерживается
IE_BYTESIZEОшибка при определении размера передаваемых и принимаемых данных
IE_DEFAULTНе поддерживаются характеристики порта, принятые по умолчанию
IE_HARDWAREПорт недоступен. Возможно, порт уже используется другим приложением
IE_MEMORYНе хватает оперативной памяти для размещения входной и выходной очередей COM-порта
IE_NOPENПорт не открыт
IE_OPENПорт уже открыт

Функцию 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. Функция CloseComm

COM-порт является аппаратным ресурсом компьютера. Когда приложение открывает 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	

// Прототип функции
BYTE FAR FindCOMPorts( void );

// ============================================================
// Функция WinMain
// ============================================================
#pragma argsused

int PASCAL
WinMain( HINSTANCE hInstance,
			HINSTANCE hPrevInstance,
			LPSTR lpCmdLine,
			int nCmdShow )
{
	char  szMsg[ 60 ] = "";
	BYTE  bFindPort;

	// Определяем доступные для использования COM-порты
	bFindPort = FindCOMPorts();

	// Формируем строку результата с названиями доступных портов
	for(int nBitNum = 0; nBitNum < 8; nBitNum++)
	{
		if (bFindPort & (BYTE) (1 << (BYTE)nBitNum))
		wsprintf( szMsg, "%sCOM%d ", (LPSTR) szMsg, nBitNum + 1 ) ;
	}

	// Отображаем на экране сообщение с названиями доступных
	// COM-портов
	MessageBox(NULL, szMsg, "Обнаружены COM-порты:",
				  MB_OK | MB_ICONINFORMATION);

	return 0;
}

// ============================================================
// Функция FindCOMPorts вызывает функцию OpenComm
// и определяет, какие COM-порты установлены в компьютере и
// доступны для использования
// ============================================================
BYTE FAR FindCOMPorts( void )
{
	// Буфер для подготовки имени порта
	char	szCommPattern[8];	
	// Идентификатор COM-порта
	int	idComDev;
	// Переменная для формирования результата
	BYTE	bFindPort = 0;
	// Вспомогательная переменная nBitNum 
	int	nBitNum = 1;

	// Пробуем открыть COM-порты COM1 - COM8
	for(int i = 0; i < 8; i++) {
		
		wsprintf( szCommPattern, "COM%d", i + 1 ) ;

		// Открываем COM-порт
		idComDev = OpenComm(szCommPattern, 1024, 1024);

		if (idComDev >= 0)
		{
			// Если COM-порт n успешно открыт, тогда устанавливаем
			// в переменной bFindPort n-ый бит
			bFindPort = bFindPort | nBitNum;

			// Закрываем COM-порт
			CloseComm(idComDev);
		}
		nBitNum <<= 1;
	}

	// Возвращаем результат
	return bFindPort;
}

После запуска приложения OPENCOMM, функция WinMain вызывает функцию FindCOMPorts, определяющую список доступных COM-портов.

Функция FindCOMPorts содержит цикл, в котором открывается очередной COM-порт:

idComDev = OpenComm(szCommPattern, 1024, 1024);

Если OpenComm возвращает значение большее или равное нулю, значит COM-порт успешно открыт. В этом случае мы устанавливаем в байте bFindPort бит с номером, соответствующим номеру открытого порта. После этого мы закрываем только что открытый порт:

CloseComm(idComDev);

Затем мы переходим к проверке следующего порта. Если функция OpenComm возвращает значение, меньшее нуля, мы считаем, что порт недоступен.

Проверив в цикле COM-порты от COM1 до COM8, функция FindCOMPorts возвращает байт bFindPort. Каждый бит этого байта отвечает за свой COM-порт.

Установлен бит Порт доступен
0COM1
1COM2
2COM3
3COM4
4COM5
5COM6
6COM7
7COM8

Получив байт 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		
#include		

// Прототип функции
BYTE FAR FindCOMPorts( void );

// =========================================================
// Функция WinMain
// =========================================================
#pragma argsused

int PASCAL
WinMain( HINSTANCE hInstance,
			HINSTANCE hPrevInstance,
			LPSTR lpCmdLine,
			int nCmdShow )
{
	char  szMsg[ 60 ] = "";
	BYTE  bFindPort;

	// Определяем работает ли Windows в расширенном режиме
	if(GetWinFlags() & WF_ENHANCED)
	{
		bFindPort = FindCOMPorts();

		for (int i = 0; i < 8; i++)
		{
			if (bFindPort & (BYTE) (1 << (BYTE)i))
					wsprintf( szMsg, "%sCOM%d ", (LPSTR) szMsg, i + 1 ) ;
		}

		BWCCMessageBox(NULL,
			szMsg, "Обнаружены COM-порты:",
			MB_OK | MB_ICONINFORMATION);
	}

	else
		BWCCMessageBox(NULL,
			"Зпустите Windows в расширенном режиме", "Сообщение",
			MB_OK | MB_ICONSTOP);

	return 0;
}

После запуска приложения, WinMain вызывает функцию GetWinFlags для определения режима работы Windows. Если Windows работает в стандартном режиме, приложение выводит соответствующее сообщение и завершает свою работу.

Если Windows работает в расширенном режиме, выполняется вызов функции FindCOMPorts, определяющей список доступных COM-портов. Затем полученный список доступных портов отображается на экране.

Функция FindCOMPorts возвращает байт, каждый бит которого отвечает за свой COM-порт. Младший бит соответствует порту COM1, а старший COM8. Исходный текст функции FindCOMPorts представлен в листинге 7.4.

Листинг 7.4. Файл COMM_DRV.CPP

#include	
#include	"comm_drv.h"

// Идентификатор драйвера асинхронного последовательного 
// адаптера
#define	VCD_Device_ID  0x0E

// ===========================================================
// Функция FindCOMPorts вызывает драйвер асинхронного адаптера 
// и определяет какие COM-порты установлены в компьютере и  
// доступны для использования
// ===========================================================
BYTE FAR FindCOMPorts( void )
{
	BYTE     bResult;
	FARPROC  lpCOMM;

	asm {

		// Определяем адрес точки входа программного интерфейса
		// драйвера асинхронного последовательного адаптера
		mov   ax, 1684h
		mov   bx, VCD_Device_ID
		xor   di, di
		mov   es, di
		int   2Fh

		// Проверяем значение регистров ES:DI
		mov   ax, es
		or    ax, di

		// Если в регистрах ES:DI записаны нулевые значения,
		// драйвер не имеет программного интерфейса и не доступен
		jz    FunctionFailed

		// Драйвер доступен, сохраняем адрес его интерфейса в
		// переменной lpCOMM
		mov   word ptr lpCOMM,	di
		mov   word ptr lpCOMM+2, es

		// Вызываем функцию из драйвера COM-порта
		mov   dx, 1
		call  dword ptr lpCOMM

		// Сохраняем результат, полученный в регистре AL
		mov   bResult, al

		// Завершаем выполнение функции
		jmp   FunctionQuit
	}

	FunctionFailed:

	asm {

		// В случае возникновения ошибки возвращаем нулевое 
		// значение
		mov   bResult, 0
	}

	FunctionQuit:

	return( bResult ) ;
}

В исходном тексте функции 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_1100xFF10110
CBR_3000xFF11300
CBR_6000xFF12600
CBR_12000xFF131200
CBR_24000xFF142400
CBR_48000xFF154800
CBR_96000xFF169600
CBR_144000xFF1714400
CBR_192000xFF1819200
CBR_384000xFF1B38400
CBR_560000xFF1F56000
CBR_1280000xFF23128000
CBR_2560000xFF27256000

Как видите, старший байт для констант CBR_ содержит значение 0xFF. Поэтому, если вы желаете записать в поле BaudRate абсолютное значение скорости, указывайте числа, меньшие, чем значение константы CBR_110 (0xFF10), и большие 2.

Если вы запишите в поле BaudRate неправильное значение, функция SetCommState завершится с ошибкой IE_BAUDRATE.

Поле ByteSize

В поле ByteSize определяется количество бит в символах, передаваемых и принимаемых через COM-порт. Это поле может содержать любое значение от 4 до 8. Если вы запишете в него другое значение, то функция SetCommState завершится с ошибкой IE_BYTESIZE.

Поле Parity

Поле Parity управляет контролем четности и может содержать одну из пяти констант, перечисленных ниже:

ЗначениеСмысл
EVENPARITYВыполняется проверка на четность
MARKPARITYБит четности всегда установлен
NOPARITYПроверка на четность не выполняется
ODDPARITYВыполняется проверка на нечетность
SPACEPARITYБит четности всегда сброшен

Если записать в это поле другое значение, функция SetCommState вернет код ошибки IE_DEFAULT.

Поле StopBits

Поле StopBits задает количество стоповых бит. Поле может содержать одну из констант, перечисленных ниже:

ЗначениеСмысл
ONESTOPBIT1 стоповый бит
ONE5STOPBITS1.5 стоповых бита
TWOSTOPBITS2 стоповых бита

Если записать в это поле другое значение, то функция 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:

Поле fRtsDisableПоле fRtsFlowСостояние сигнала RTS
00Установлен
01Разрешено управление потоком по линии RTS
10Сброшен
11Сброшен

Если сигналы 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 при использовании основного определения:

ПолеОписание
statusОпределяет состояние передачи данных. Оно может содержать один или несколько флагов:

CSTF_CTSHOLD

Передача данных приостановлена в ожидании сигнала CTS

CSTF_DSRHOLD

Передача данных приостановлена в ожидании сигнала DSR

CSTF_RLSDHOLD

Передача данных приостановлена в ожидании сигнала RLSD

CSTF_XOFFHOLD Передача данных приостановлена после приема символа XOFF

CSTF_XOFFSENT

Передача данных приостановлена при передаче символа XOFF. Передача приостанавливается, когда символ XOFF передан. CSTF_XOFFSENT используется системами, которые принимают следующий символ XON вне зависимости от настоящего принятого символа

CSTF_EOF

Получен символ конца файла EOF

CSTF_TXIM

Произошла задержка при передаче символа
cbInQueКоличество символов, находящихся во входной очереди
cbOutQueКоличество символов в выходной очереди

Функция GetCommError возвращает значение, являющееся комбинацией флагов. Флаги определяют ошибки, возникшие при последнем вызове коммуникационной функции.

Возвращаемое значение может быть комбинацией из следующих флагов:

ФлагОписание
CE_BREAKОбнаружено состояние разрыва связи (BREAK)
CE_CTSTOТайм-аут CTS. Во время передачи символа сигнал CTS отсутствовал дольше промежутка времени, определенного полем fCtsHold структуры COMSTAT
CE_DNSПараллельный порт не был выбран
CE_DSRTOТайм-аут DSR. Во время передачи символа сигнал DSR отсутствовал промежуток времени, определенный полем fDsrHold структуры COMSTAT
CE_FRAMEОбнаружена ошибка формата кадра (framing error)
CE_IOEВо время попытки взаимодействия с параллельным портом произошла ошибка ввода/вывода
CE_MODEЗапрашиваемый режим не поддерживается или идентификатор COM-порта недействителен. Если установлен этот флаг, другие флаги следует игнорировать
CE_OOPПараллельный адаптер передал компьютеру сигнал "out of paper" - конец бумаги
CE_OVERRUNСимвол не был прочитан из регистров COM-порта до прихода следующего символа. В результате этот символ был потерян
CE_PTOИстекло время (тайм-аут) при выполнении попытки взаимодействия с параллельным устройством
CE_RLSDTOТайм-аут RLSD. Во время передачи символа сигнал RLSD отсутствовал промежуток времени, определенный полем fRlsdHold структуры COMSTAT
CE_RXOVERОчередь приемника переполнена. Либо очередь приемника переполнена, либо символ был получен после получения символа конца файла
CE_RXPARITYОбнаружена ошибка четности
CE_TXFULLОчередь передатчика полностью заполнена, но функция пытается записать новые данные в эту очередь

7.2.13. Функция SetCommEventMask

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

Функция SetCommEventMask разрешает регистрацию изменений состояния COM-порта в слове событий данного COM-порта:

UINT FAR* SetCommEventMask(int idComDev, UINT fuEvtMask);

Параметр idComDev является идентификатором COM-порта. Это значение возвращает функция OpenComm.

Параметр fuEvtMask определяет, какие события будут разрешены. Этот параметр может быть комбинацией из любых констант, перечисленных в следующей таблице:

КонстантаСмысл
EV_BREAKУстанавливается при обнаружении состояния BREAK на входной линии
EV_CTSУстанавливается, когда сигнал CTS изменяет свое состояние
EV_CTSSУказывает текущее состояние сигнала CTS
EV_DSRУстанавливается, когда сигнал DSR изменяет свое состояние
EV_ERRУстанавливается при возникновении ошибки на линии. Такими ошибками являются CE_FRAME, CE_OVERRUN, и CE_RXPARITY (см. описание функции GetCommError)
EV_PERRУстанавливается при возникновении ошибки принтера для параллельного порта. Такими ошибками являются CE_DNS, CE_IOE, CE_LOOP и CE_PTO (см. описание функции GetCommError)
EV_RINGУказывает текущее состояние сигнала RI во время последнего прерывания от модема
EV_RLSDУстанавливается, когда сигнал RLSD изменяет свое состояние
EV_RLSDSУказывает текущее состояние сигнала RLSD
EV_RXCHARУстанавливается, когда символ принимается и помещается во входную очередь
EV_RXFLAGУстанавливается, когда определенный в структуре DCB символ принимается и помещается во входную очередь COM-порта
EV_TXEMPTYУстанавливается, когда последний символ из выходной очереди отправлен в COM-порт и выходная очередь становится пустой

Функция 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. Это число может быть комбинацией следующих кодов извещения:

Код извещенияОписание
CN_EVENTПроизошло событие, разрешенное в слове событий COM-порта. Эти события разрешаются вызовом функции SetCommEventMask. После прихода сообщения WM_COMMNOTIFY с кодом извещения CN_EVENT приложение должно вызвать функцию GetCommEventMask, чтобы определить, какое именно событие произошло и сбросить событие
CN_RECEIVEОчередь приемника содержит больше, чем cbWriteNotify байт. Пороговое значение cbWriteNotify задается одним из параметров функции EnableCommNotification
CN_TRANSMITОчередь передатчика содержит меньше, чем cbOutQueue байт. Пороговое значение cbOutQueue задается одним из параметров функции EnableCommNotification

Если приложение обработало поступившее ему сообщение WM_COMMNOTIFY, оно должно возвратить нулевое значение.

Сообщение WM_COMMNOTIFY с кодом извещения CN_EVENT передается только тогда, когда слово состояния событий изменяет свое значение. Приложение, которое принимает сообщение WM_COMMNOTIFY, должно каждый раз очищать слово состояния событий, для того чтобы гарантировать получение сообщений в дальнейшем.

Использование функции EnableCommNotification для разрешения передачи сообщений WM_COMMNOTIFY с кодами извещения CN_RECEIVE и CN_TRANSMIT может вызвать генерацию ошибочных сообщений WM_COMMNOTIFY, для которых параметр NotifyStatus равен нулю. При обмене данными на высоких скоростях это может привести к аварийной ситуации и даже перезагрузке компьютера.

Для решения этой проблемы можно использовать следующие методы:

  • Игнорировать все сообщения WM_COMMNOTIFY, для которых параметр NotifyStatus равен нулю
  • Не использовать сообщения WM_COMMNOTIFY для событий CN_RECEIVE и CN_TRANSMIT
  • Разрешить сообщение CN_EVENT для события EV_RXCHAR с помощью функций SetCommEventMask и EnableCommNotification. Сообщение формируется при поступлении во входную очередь очередного символа. Функцию EnableCommNotification следует вызывать с параметрами cbWriteNotify и cbOutQueue -1, чтобы сообщения CN_RECEIVE и CN_TRANSMIT не посылались. Этот метод продемонстрирован в приложении TTY из Windows Software Development Kit (SDK)
  • Вместо использования функции EnableCommNotification приложение должно само периодически опрашивать входной буфер на наличие в нем принятых данных. Опрос можно производить по таймеру или в цикле обработки сообщений
  • Заменить драйвер последовательного асинхронного адаптера COMM.DRV.
  • Событие 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-портов и проводит проверку тайм-аута. Период таймера изменить нельзя.

    [Назад] [Содеожание] [Дальше]