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

Модемы и факс-модемы. Программирование для MS-DOS и Windows.

© Александр Фролов, Григорий Фролов
Том 16, М.: Диалог-МИФИ, 1993.

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

7.4. Приложение PHONE

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

Приложение PHONE создано специально для работы с модемами и демонстрирует приемы передачи модему AT-команд и автоматического реагирования на ответ модема. Главная диалоговая панель приложения представляет собой панель телефонного аппарата и показана на рисунке 7.3.

Рис. 7.3. Приложение PHONE

Вы можете набрать на клавиатуре, расположенной в левой части диалоговой панели "Телефон", любой телефонный номер. Введенный номер отображается в специальном поле справа вверху. Если цифра номера введена неправильно, ее можно стереть, нажав на кнопку "<-".

Чтобы модем приступил к набору номера, нажмите кнопку "Набор". Если после набора номера произошло соединение с удаленным модемом, вы можете разорвать связь и повесить трубку, нажав кнопку "Сброс". По окончании работы с приложением, его можно завершить, нажав на кнопку "Выход". Во время инициализации модема, набора номера и попытки установления связи с удаленным модемом завершение приложения не разрешается и кнопка "Выход" блокируется.

Перед тем как запустить приложение PHONE на вашем компьютере, следует создать в каталоге Windows файл PHONE.INI (см. листинг 7.9).

Листинг 7.9. Файл PHONE.INI

[Port]
Mode=COM2:19200,N,8,1
DsrDtrFlow=1
CtsRtsFlow=1
RtsDisable=0
DtrDisable=0

[Modem]
Init=ATQ0V1X4&C1&D2M1
Dial=ATDP
HangUp=ATH0
ModemTimeout=5000
DialTimeout=60000
HangUpTimeout=2000

Файл PHONE.INI должен содержать раздел [Port]. В этом разделе расположены строки, определяющие используемый модемом COM-порт, скорость обмена, формат передаваемых данных, режим управления потоком.

Строка Mode определяет используемый COM-порт, скорость обмена и формат передаваемых данных. Формат строки Mode совпадает с форматом команды MODE операционной системы MS-DOS.

Строки DsrDtrFlow, CtsRtsFlow, RtsDisable и DtrDisable используется для указания режима управления потоком. Числа, указанное вами в этих строках, будут записаны в соответствующие поля структуры DCB.

Далее должен находиться раздел [Modem]. В нем расположены строки, содержащие AT-команды инициализации модема и определены интервалы времени, отведенные на выполнение различных операций.

СтрокаОписание
InitКоманда инициализации модема. Инициализация выполняется один раз при запуске приложения PHONE
DialКоманда набора номера. Передается модему по нажатию кнопки "Набор"
HangUpКоманда разрыва связи. Передается модему по нажатию кнопки "Сброс"
ModemTimeoutПромежуток времени, в течение которого модем должен отреагировать на передачу ему AT-команды
DialTimeoutПромежуток времени, в течение которого модем должен набрать номер и установить связь с удаленным модемом
HangUpTimeoutВеличина задержки перед и после передачи модему Escape-последовательности. Используется для переключения модема в командный режим

Временные интервалы, определяемые строками ModemTimeout, DialTimeout и HangUpTimeout, должны быть указаны в миллисекундах.

Исходный текст главного файла приложения PHONE приведен в листинге 7.10.

Листинг 7.10. Файл PHONE.CPP

#define 	STRICT

#include	
#include	
#include	
#include	
#include	
#include	"phone.h"

// Глобальные переменные
HINSTANCE	hInst;				// Идентификатор приложения
int				idComDev;		// Идентификатор COM-порта
char				sBufNum[20];	// Буфер для телефонного номера
BOOL				WaitFlag = FALSE;

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

int PASCAL
WinMain(	HINSTANCE	hInstance,
				HINSTANCE	hPrevInstance,
				LPSTR    	lpszCmdLine,
				int      	nCmdShow)
{
	static DLGPROC lpfnDlgProc;

	hInst = hInstance;

	// Переходник для функции диалоговой панели
	lpfnDlgProc = 
		(DLGPROC)MakeProcInstance((FARPROC)DlgProc, hInst);

	// Создаем модальную диалоговую панель
	DialogBox( hInstance, "PHONE", NULL, lpfnDlgProc );

	return 0;
}

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

BOOL CALLBACK _export
DlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
	int	iErr;

	switch(msg)
	{
		// Инициализация диалоговой панели
		case WM_INITDIALOG:
		{
			// Посылаем сообщение WM_CONNECT
			PostMessage( hdlg,WM_CONNECT,0,0L );
			return TRUE;
		}

		case WM_CONNECT:
		{
			EnableWindow(GetDlgItem(hdlg, IDCANCEL), FALSE);

			// Открываем COM-порт и инициализируем модем
			iErr = InitLine();

			EnableWindow(GetDlgItem(hdlg, IDCANCEL), TRUE);

			// Если возникла ошибка, отображаем сообщение
			// и закрываем диалоговую панель
			if( iErr < 0 )
			{
				ShowError( iErr );
				EndDialog( hdlg, 0 );
			}
			return TRUE;
		}

		case WM_COMMAND:
		{
			switch(wParam)
			{
				// Нажата кнопка номеронаберателя
				case	ID_1: case	ID_2: case	ID_3: case	ID_4: case	ID_5:
				case	ID_6:	case	ID_7: case	ID_8: case	ID_9: case	ID_0:
				{
					int	iNum;
					char	sTmp[10];

					// Определяем набранную цифру
					iNum = wParam - ID_0;

					wsprintf( sTmp, "%d", iNum );

					// Добавляем набранную цифру к номеру
					lstrcat(sBufNum, sTmp);

					// Обновляем номер на экране
					SetDlgItemText(hdlg, ID_NUMBER, sBufNum);

					return TRUE;
				}

				// Нажата кнопка "Сброс"
				case ID_CLEAR:
				{
					EnableWindow(GetDlgItem(hdlg, IDCANCEL), FALSE);

					// Вешаем трубку
					HangUpPhone();

					EnableWindow(GetDlgItem(hdlg, IDCANCEL), TRUE);

					return TRUE;
				}

				// Удаляем последную цифру номера
				case ID_BACK:
				{
					if( lstrlen( sBufNum ) != 0 )
					{
						sBufNum[lstrlen( sBufNum ) - 1] = '\0';
						SetDlgItemText( hdlg, ID_NUMBER, sBufNum );
					}
					else MessageBeep( MB_ICONASTERISK );

					return TRUE;
				}

				// Нажата кнопка "Набор"
				case ID_DIAL:
				{
					EnableWindow(GetDlgItem(hdlg, IDCANCEL), FALSE);

					// Набираем номер
					iErr = DialPhone();

					// Если возникла ошибка, отображаем сообщение
					if( iErr < 0 )
						ShowError( iErr );

					EnableWindow(GetDlgItem(hdlg, IDCANCEL), TRUE);
					return TRUE;
				}

				// Нажата кнопка "Выход"
				case IDCANCEL:
				{
					// Закрываем COM-порт
					CloseLine();

					// Закрываем диалоговую панель
					EndDialog( hdlg, 0 );

					return TRUE;
				}
			}
		}
	}
	return FALSE;
}

// ============================================================
// Функция InitLine
// ============================================================
int	InitLine()
{
	BYTE	bSet;
	DCB	dcb;
	int	iErr;
	char	sTmp[200];
	char	szModemAnswer[200];

	char	szInitMode[80];
	char	szTmpStrBool[3];
	char	szInitModem[100];
	long	lnModemTimeout;

	// Определяем режим работы COM-порта. Для этого считываем
	// строку Mode из раздела Port файла PHONE.INI
	GetPrivateProfileString( "Port", "Mode", "COM1:9600,n,8,1",
			szInitMode, sizeof(szInitMode), "phone.ini" );

	// Открываем COM-порт, заданный в строке szInitMode
	wsprintf( sTmp, "COM%c", szInitMode[3] );

	idComDev = OpenComm( sTmp, 4915, 4915 );
	if (idComDev < 0)
		return ERR_NO_OPEN;

	// Заполняем структуру DCB в соответствии с szInitMode
	iErr = BuildCommDCB( szInitMode, &dcb );
	if(iErr < 0)
		return ERR_DCB_BUILD;

	// Изменяем отдельные поля структуры DCB в
	// соответствии с файлом phone.ini

	// Заполняем поля fOutxDsrFlow, fDtrflow, DsrTimeout
	GetPrivateProfileString( "Port", "DsrDtrFlow", "1",
									 szTmpStrBool, sizeof(szTmpStrBool),
									 "phone.ini" );
	
	dcb.fOutxDsrFlow = dcb.fDtrflow = bSet =
									(BYTE) atoi(szTmpStrBool);

	dcb.DsrTimeout = (bSet) ? 3 : 0 ;

	// Заполняем поля fOutxCtsFlow, fRtsflow, CtsTimeout
	GetPrivateProfileString( "Port", "CtsRtsFlow", "1",
		 szTmpStrBool, sizeof(szTmpStrBool), "phone.ini" );

	dcb.fOutxCtsFlow = dcb.fRtsflow = bSet =
									(BYTE) atoi(szTmpStrBool);

	dcb.CtsTimeout = (bSet) ? 3 : 0 ;

	// Заполняем поле fRtsDisable
	GetPrivateProfileString( "Port", "RtsDisable", "0",
		 szTmpStrBool, sizeof(szTmpStrBool), "phone.ini" );

	dcb.fRtsDisable = (BOOL) atoi(szTmpStrBool);

	// Заполняем поле fDtrDisable
	GetPrivateProfileString( "Port", "DtrDisable", "0",
		 szTmpStrBool, sizeof(szTmpStrBool), "phone.ini" );
	
	dcb.fDtrDisable = (BOOL) atoi(szTmpStrBool);

	// Устанавливаем новый режим работы COM-порта
	iErr = SetCommState( &dcb );
	if( iErr < 0 )
		return ERR_DCB_SET;

	// Удаляем данные из входной и выходной очередей COM-порта
	FlushComm(idComDev, 1);
	FlushComm(idComDev, 0);

	// Подаем сигнал DTR
	EscapeCommFunction(idComDev, SETDTR);

	// Определяем команду инициализации модема, для этого 
	// считываем строку Init из раздела Modem файла phone.ini
	GetPrivateProfileString( "Modem", "Init", "ATZ",
		 szInitModem, sizeof(szInitModem), "phone.ini" );

	// Добавляем к команде символ перевода строки
	wsprintf( sTmp, "%s\r", szInitModem );

	// Передаем команду инициализации модему
	iErr = WriteComm( idComDev, sTmp, lstrlen( sTmp ) );
	if( iErr < 0 )
		return ERR_WRITE;

	// Определяем время ожидания ответа от модема
	GetPrivateProfileString( "Modem", "ModemTimeout", "1000",
		 sTmp, sizeof(sTmp), "phone.ini" );

	lnModemTimeout = atol( sTmp );

	// Ожидаем от модема ответ "OK"
	iErr = WaitModemAnswer(	idComDev, (LPSTR*)szOkString,
								szModemAnswer, 200, (DWORD)lnModemTimeout);
	if( iErr < 0 )
		return iErr;

	return 0;
}

// ============================================================
// Функция DialPhone
// ============================================================
int	DialPhone()
{
	int	iErr;
	char	sTmp[80];
	char	szModemAnswer[200];
	char	szDialModem[80];
	long	lnModemTimeout;

	// Определяем команду набора номера. Для этого считываем
	// строку Dial из раздела Modem файла phone.ini
	GetPrivateProfileString( "Modem", "Dial", "ATDP",
		 szDialModem, sizeof(szDialModem), "phone.ini" );

	// Формируем во временном буфере sTmp команду набора номера
	wsprintf( sTmp, "%s%s\r", szDialModem, sBufNum );

	// Удаляем данные из входной и выходной очередей COM-порта
	FlushComm(idComDev, 1);
	FlushComm(idComDev, 0);

	// Передаем модему команду набора номера
	iErr = WriteComm(idComDev, sTmp, lstrlen(sTmp) );
	if( iErr < 0 )
		return ERR_WRITE;

	// Определяем время ожидания ответа от модема
	GetPrivateProfileString( "Modem", "DialTimeout", "30000",
		 sTmp, sizeof(sTmp), "phone.ini" );

	lnModemTimeout = atol( sTmp );

	// Ожидаем ответ от модема
	iErr = WaitModemAnswer(	idComDev, (LPSTR*)szAnswer,
							szModemAnswer, 200, (DWORD)lnModemTimeout);

	BWCCMessageBox(NULL, szModemAnswer, "Ответ модема", MB_OK );

	return iErr;
}

// ============================================================
// Функция CloseLine
// ============================================================
int	CloseLine()
{
	// Сбрасывааем сигнал DTR
	EscapeCommFunction(idComDev, CLRDTR);

	// Удаляем данные из входной и выходной очередей COM-порта
	FlushComm(idComDev, 1);
	FlushComm(idComDev, 0);

	// Закрываем COM-порт
	CloseComm(idComDev);

	return 0;
}

// ============================================================
// Функция HangUp
// ============================================================
int	HangUpPhone()
{
	DWORD	StartTick;
	char		szHangUpString[80], sTmp[80];
	long		lnModemTimeout;

	// Определяем время задержки перед подачей команды "+++"
	GetPrivateProfileString( "Modem", "HangUpTimeout", "2000",
		 sTmp, sizeof(sTmp), "phone.ini" );

	lnModemTimeout = atol( sTmp );

	// Определяем команду разрыва связи. Для этого считываем
	// строку Dial из раздела Modem файла PHONE.INI
	GetPrivateProfileString( "Modem", "HangUp", "ATH0",
		 szHangUpString, sizeof(szHangUpString), "phone.ini" );

	// Формируем во временном буфере sTmp команду 
	// разрыва соединения
	wsprintf( sTmp, "%s\r", szHangUpString );

	// Определяем текущий момент времени
	StartTick = GetTickCount();

	// Формируем задержку
	while( StartTick + (DWORD)lnModemTimeout > GetTickCount() )
		Idle();

	// Передаем модему последовательность "+++" для перевода его
	// в командный режим
	WriteComm( idComDev, "+++", 3);

	// Определяем текущий момент времени
	StartTick = GetTickCount();

	// Формируем задержку
	while( StartTick + (DWORD)lnModemTimeout > GetTickCount() )
		Idle();

	// Передаем модему команду разорвать соединение и 
	// повесить трубку
	WriteComm( idComDev, sTmp, lstrlen( sTmp ) );

	EscapeCommFunction(idComDev, CLRDTR);

	return 0;
}

// ============================================================
// Функция ShowError
// ============================================================
void	 ShowError( int iErr )
{
	int	iNum;
	char	szMsgBuff[40];

	// Неизвестная ошибка
	if(( iErr < -6 ) || ( iErr >= 0 ))
		return;

	// Загружаем из ресурсов приложения строку
	// с идентификатором iErr
	LoadString( hInst, iErr, szMsgBuff, 40 );

	// Отображаем на экране сообщение об ошибке
	BWCCMessageBox( NULL, szMsgBuff, "Ошибка",
						 MB_OK | MB_ICONSTOP	);

	// Подаем звуковой сигнал
	MessageBeep( MB_ICONASTERISK );

	return;
}

// ============================================================
// Функция ReadComPort
// ============================================================
int ReadComPort(int idComDev, LPSTR szDest, int nLength)
{

	COMSTAT ComStat;
	int nTotalRead = 0, nRead = 0;

	while(nLength > nTotalRead)
	{
		// Определяем состояние COM-порта
		GetCommError(idComDev,&ComStat);

		// Во входной очереди нет данных
		if (ComStat.cbInQue == 0)
			break;

		// Считываем данные в буфер szDest
		else
			nRead = ReadComm(idComDev,&(szDest[nTotalRead]),
													nLength - nTotalRead);
		// Если функция ReadComm завершилась с 
		// ошибкой, возвращаем -1
		if (nRead < 0)
			return -1;

		nTotalRead += nRead;
	}

	// Возвращаем количество байт, прочитанных из COM-порта
	return nTotalRead;
}

// ============================================================
// Функция WaitModemAnswer
// ============================================================
int WaitModemAnswer(int idComDev, LPSTR *szWaitDest,
						  LPSTR szModemAnswer, int nLength,
						  DWORD dwTimeOut)
{
	int	nRead;
	int	nTotalChar = 0, i;
	DWORD	dwStartTick;
	BOOL	fFind = FALSE;

	// Определяем текущий момент времени
	dwStartTick = GetTickCount();

	do
	{
		// Считываем данные из COM-порта
		nRead = ReadComPort(idComDev, &szModemAnswer[nTotalChar],
							nLength - lstrlen(szModemAnswer));

		// В случае ошибки возвращаем ERR_READ
		if(nRead < 0)
			return ERR_READ;

		else if(nRead > 0)
		{
			nTotalChar += nRead;

			// Добавляем в конец полученных данных двоичный ноль
			szModemAnswer[nTotalChar] = '\0';

			// Проверяем, получен ли от модема ответ из 
			// массива szWaitDest
			for(int i = 0; szWaitDest[i]; i++)
			{
				if(strstr(szModemAnswer, szWaitDest[i]))
				{
					// Если ответ получен, завершаем чтение данных
					// и возвращаем управвление
					fFind = TRUE;
					break;
				}
			}
		}

		// Разрешаем обработку сообщений для других приложений
		else
			Idle();

	// Выходим из цикла, если от модема получен ожидаемый ответ,
	// возникла ошибка при чтении данных, или истекло время
	// ожидания ответа (nTimeOut * 1000)

	} while (!fFind && nRead >= 0 &&
				dwStartTick + dwTimeOut > GetTickCount() );

	if(nRead >= 0 && !fFind)
		return ERR_TIMEOUT;

	// Возвращаем количесттво пррочитанных символов
	return lstrlen( szModemAnswer );
}

// ============================================================
// Функция Idle
// ============================================================
void	Idle(void)
{
	MSG msg;

	while( PeekMessage( &msg, NULL, NULL, NULL, PM_REMOVE ) )
	{
		TranslateMessage( &msg );
		DispatchMessage( &msg );
	}

	return;
}

Функция WinMain, приложения PHONE, содержит всего несколько строк. В ней нет привычного вызова функций регистрации класса окна и создания окна. Вместо этого WinMain сразу выводит на экран модальную диалоговую панель PHONE, определенную в файле ресурсов приложения PHONE.RC.

Для создания модальной диалоговой панели мы воспользовались функцией DialogBox. Перед вызовом функции DialogBox вызывается функция MakeProcInstance. Она создает переходник для функции диалога DlgProc. Более подробную информацию о создании диалоговых панелей можно получить в 12 томе "Библиотеки системного программиста".

#pragma argsused

int PASCAL
WinMain(HINSTANCE hInstance,
	HINSTANCE hPrevInstance,
	LPSTR     lpszCmdLine,
	int       nCmdShow)
{
	static DLGPROC lpfnDlgProc;

	hInst = hInstance;

	// Переходник для функции диалоговой панели
	lpfnDlgProc = 
		(DLGPROC)MakeProcInstance((FARPROC)DlgProc, hInst);

	// Создаем модальную диалоговую панель
	DialogBox( hInstance, "PHONE", NULL, lpfnDlgProc );

	return 0;
}

Функция диалоговой панели DlgProc обрабатывает сообщения WM_INITDIALOG, WM_CONNECT, WM_COMMAND.

Обработчик сообщения WM_INITDIALOG посылает функции диалоговой панели DlgProc сообщение WM_CONNECT и возвращает значение TRUE:

PostMessage( hdlg,WM_CONNECT,0,0L );
return TRUE;

Сообщение WM_CONNECT определено во включаемом файле PHONE.H следующим образом:

#define WM_CONNECT	WM_USER

Обработчик сообщения WM_CONNECT блокирует кнопку "Сброс" на время инициализации модема, вызывая функцию EnableWindow. После блокировки кнопки "Сброс" вызывается функция InitLine.

// Блокируем кнопку "Сброс"
EnableWindow(GetDlgItem(hdlg, IDCANCEL), FALSE);

// Открываем COM-порт и инициализируем модем
iErr = InitLine();

// Разблокируем кнопку "Сброс"
EnableWindow(GetDlgItem(hdlg, IDCANCEL), TRUE);

Функция InitLine, определенная в приложении, открывает COM-порт, устанавливает режим его работы. После этого InitLine передает модему команду инициализации и ждет ответ в течении некоторого времени.

Если модем не возвратил ответ OK, функция InitLine возвращает отрицательную величину. В этом случае приложение сначала вызывает функцию ShowError, которая отображает сообщение об ошибке, а затем функцию EndDialog, которая закрывает диалоговую панель.

// Если возникла ошибка отображаем сообщение
// и закрываем диалоговую панель
if( iErr < 0 )
{
	ShowError( iErr );
	EndDialog( hdlg, 0 );
}

Когда пользователь нажимает на любую кнопку диалоговой панели PHONE, вызывается обработчик сообщения WM_COMMAND. Параметр wParam сообщения WM_COMMAND содержит идентификатор нажатой кнопки. В зависимости от его значения обработчик WM_COMMAND выполняет различные действия.

Если нажата одна из цифровых кнопок "1", "2", "3" ... "0", соответствующая цифра добавляется в конец буфера sBufNum. В этом буфере подготавливается строка с телефонным номером.

Чтобы стереть последнюю цифру номера, можно нажать кнопку "<-". Эта кнопка имеет идентификатор ID_BACK. Обработчик сообщений WM_COMMAND, имеющий параметр wParam, равный ID_BACK, стирает последнюю цифру из буфер sBufNum. Если все символы из буфера sBufNum удалены, вызывается функция MessageBeep, подающая звуковой сигнал.

Набор телефонного номера, записанного в буфере sBufNum, происходит после нажатия на кнопку "Набор". В этом случае в функцию окна приходит сообщение WM_COMMAND с параметром wParam, равным ID_DIAL. Обработчик этого сообщения блокирует кнопку "Выход" и вызывает функцию DialPhone, определенную в приложении, которая и производит набор номера.

Если во время набора номера возникла ошибка, функция DialPhone возвращает отрицательное значение. В этом случае вызывается функция ShowError, которая отображает на экране диалоговую панель с кратким описанием возникшей ошибки. Перед окончанием обработки сообщения разблокируется кнопка "Выход".

case ID_DIAL:
{
	EnableWindow(GetDlgItem(hdlg, IDCANCEL), FALSE);
	// Набираем номер
	iErr = DialPhone();

	// Если возникла ошибка, отображаем сообщение
	if( iErr < 0 )
		ShowError( iErr );

	EnableWindow(GetDlgItem(hdlg, IDCANCEL), TRUE);
	return TRUE;
}

Чтобы завершить приложение, надо нажать кнопку "Выход". В этом случае в функцию окна приходит сообщение WM_COMMAND с параметром wParam равным IDCANCEL. Обработчик этого сообщения закрывает COM-порт, вызывая функцию CloseLine.

Затем он вызывает функцию EndDialog, которая закрывает диалоговую панель и завершает приложение.

case IDCANCEL: { // Закрываем COM-порт CloseLine(); // Закрываем диалоговую панель EndDialog( hdlg, 0 ); return 0; }

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

case ID_CLEAR:
{
	EnableWindow(GetDlgItem(hdlg, IDCANCEL), FALSE);

	// Вешаем трубку
	HangUpPhone();

	EnableWindow(GetDlgItem(hdlg, IDCANCEL), TRUE);

	return TRUE;
}

Он блокирует кнопку "Выход", а затем вызывает функцию HangUpPhone. Функция HangUpPhone вешает трубку и разрывает связь с удаленным модемом.

Коды ошибок, идентификаторы диалоговой панели PHONE, а также два массива строк szOkString и szAnswer определены в файле PHONE.H (см. листинг 7.11).

Листинг 7.11. Файл PHONE.H

// Прототипы функций
BOOL	CALLBACK _export DlgProc(HWND, UINT, WPARAM, LPARAM);

int	InitLine(void);
int	DialPhone(void);
int	CloseLine(void);
int	HangUpPhone(void);
void	ShowError(int iErr);
void	Idle(void);
int	WaitModemAnswer(int idComDev, LPSTR *szWaitDest,
							 LPSTR szModemAnswer, int nLength,
							 DWORD dwTimeOut);

// Определение констант
#define	WM_CONNECT WM_USER

#define	ERR_NO_OPEN		(-1)	
#define	ERR_DCB_BUILD	(-2)	
#define	ERR_DCB_SET		(-3)
#define	ERR_WRITE		(-4)
#define	ERR_TIMEOUT		(-5)
#define	ERR_READ			(-6)

#define ID_0 100
#define ID_1 101
#define ID_2 102
#define ID_3 103
#define ID_4 104
#define ID_5 105
#define ID_6 106
#define ID_7 107
#define ID_8 108
#define ID_9 109
#define ID_REPEAT 111
#define ID_CLEAR	130
#define ID_BACK	123
#define ID_NUMBER	120
#define ID_DIAL	122

// Определение массивов строк szOkString и szAnswer
char *szOkString[] = {	"OK", 	NULL };

char *szAnswer[] = {
										"OK",		"CONNECT",
										"RING",	"NO CARRIER",
										"ERROR",	"NO DIAL TONE",
										"BUSY",	"NO ANSWER",
										NULL
									};

В листинге 7.12 представлен исходный текст файла PHONE.RC, содержащего описание ресурсов приложения. В нем описаны диалоговая панель PHONE, пиктограмма PHONE и таблица строк.

Листинг 7.12. Файл PHONE.RC

#include "phone.h"

PHONE DIALOG 59, 29, 131, 79
STYLE WS_POPUP | WS_VISIBLE | WS_BORDER
CLASS "BorDlg"
CAPTION "Телефон"
BEGIN
	PUSHBUTTON "1", ID_1, 8, 9, 14, 14, 
		WS_CHILD | WS_VISIBLE | WS_TABSTOP
	PUSHBUTTON "2", ID_2, 24, 9, 14, 14, 
		WS_CHILD | WS_VISIBLE | WS_TABSTOP
	PUSHBUTTON "3", ID_3, 40, 9, 14, 14, 
		WS_CHILD | WS_VISIBLE | WS_TABSTOP
	PUSHBUTTON "4", ID_4, 8, 25, 14, 14, 
		WS_CHILD | WS_VISIBLE | WS_TABSTOP
	PUSHBUTTON "5", ID_5, 24, 25, 14, 14, 
		WS_CHILD | WS_VISIBLE | WS_TABSTOP
	PUSHBUTTON "6", ID_6, 40, 25, 14, 14, 
		WS_CHILD | WS_VISIBLE | WS_TABSTOP
	PUSHBUTTON "7", ID_7, 8, 41, 14, 14, 
		WS_CHILD | WS_VISIBLE | WS_TABSTOP
	PUSHBUTTON "8", ID_8, 24, 41, 14, 14, 
		WS_CHILD | WS_VISIBLE | WS_TABSTOP
	PUSHBUTTON "9", ID_9, 40, 41, 14, 14, 
		WS_CHILD | WS_VISIBLE | WS_TABSTOP
	PUSHBUTTON "0", ID_0, 24, 57, 30, 14, 
		WS_CHILD | WS_VISIBLE | WS_TABSTOP
	PUSHBUTTON "#", ID_REPEAT, 8, 57, 14, 14, 
		WS_CHILD | WS_VISIBLE | WS_TABSTOP
	PUSHBUTTON "Выход", IDCANCEL, 78, 57, 32, 14, 
		WS_CHILD | WS_VISIBLE | WS_TABSTOP
	PUSHBUTTON "Сброс", ID_CLEAR, 66, 41, 28, 14, 
		WS_CHILD | WS_VISIBLE | WS_TABSTOP
	PUSHBUTTON "Набор", ID_DIAL, 78, 25, 32, 14, 
		WS_CHILD | WS_VISIBLE | WS_TABSTOP
	CONTROL "", ID_NUMBER, "EDIT", 	ES_LEFT | ES_READONLY |
		WS_CHILD | WS_VISIBLE | WS_BORDER, 66, 10, 58, 12
	PUSHBUTTON "<--", ID_BACK, 96, 41, 28, 14, 
		WS_CHILD | WS_VISIBLE | WS_TABSTOP
	CONTROL "", 110, "BorShade", 1 | WS_CHILD | WS_VISIBLE, 
		4, 4, 54, 72
	CONTROL "", 112, "BorShade", 1 | WS_CHILD | WS_VISIBLE, 
		62, 4, 66, 72
END

PHONE ICON "phone.ico"

STRINGTABLE 
BEGIN
	ERR_NO_OPEN,		"COM-порт не открыт"
	ERR_DCB_BUILD,	"Ошибка DCB"
	ERR_DCB_SET,		"Ошибка при установке режимма COM-порта"
	ERR_WRITE,			"Ошибка при записи в COM-порта"
	ERR_TIMEOUT,		"Модем не отвечает"
	ERR_READ,			"Ошибка чтения из COM-порта"
END

В листинге 7.13 приведено изображение пиктограммы, расположенной в файле PHONE.ICO, на который ссылается оператор ICON в файле PHONE.RC.

Листинг 7.13. Файл PHONE.ICO

Файл определения модуля для приложения PHONE приведен в листинге 7.14.

Листинг 7.14. Файл PHONE.DEF

; ==========================================================
; Файл определения модуля
; ==========================================================

NAME PHONE
DESCRIPTION 'Приложение PHONE, (C) 1994, Frolov G.V.'
EXETYPE windows
STUB 'winstub.exe'
STACKSIZE	16384
HEAPSIZE		16384
CODE preload moveable discardable
DATA preload moveable multiple
[Назад] [Содеожание] [Дальше]