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

Приложение 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    <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


Содержание раздела