Приложение 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-последовательности. Используется для переключения модема в командный режим |
Исходный текст главного файла приложения PHONE приведен в листинге 7.10.
Листинг 7.10. Файл PHONE.CPP
#define STRICT
#include <windows.h>
#include <string.h>
#include <stdlib.h>
#include <mem.h>
#include <bwcc.h>
#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