Модемы и факс-модемы. Программирование для MS-DOS и Windows.
© Александр Фролов, Григорий Фролов
Том 16, М.: Диалог-МИФИ, 1993.
Приложение TELETYPE демонстрирует использование телекоммуникационных функций Windows и сообщения WM_COMMNOTIFY для работы с портами асинхронного последовательного адаптера и модемами.
Приложение TELETYPE выполняет все основные функции, которые должна поддерживать любая телекоммуникационная программа. TELETYPE позволяет передавать модему AT-команды, принимать от него ответ и отображать его на экране.
Перед тем как запускать приложение TELETYPE на вашем компьютере, следует создать в каталоге Windows файл TELETYPE.INI (см. листинг 7.15).
Листинг 7.15. Файл TELETYPE.INI
[Port]
Mode=COM2:9600,N,8,1
Файл TELETYPE.INI должен содержать раздел [Port], состоящий из единственной строки Mode. В этой строке определяется номер COM-порта, к которому подключен модем, скорость обмена и формат передаваемых данных. Формат строки Mode совпадает с форматом команды MODE операционной системы MS-DOS.
Запустите TELETYPE. Набирая на клавиатуре AT-команды модема, можно перевести его в любой режим. Можно сбросить текущую конфигурацию. Для этого введите команду ATZ и нажмите клавишу . В ответ на введенную команду модем загрузит принятую по умолчанию конфигурацию и вернет компьютеру ответ OK.
Чтобы приложение TELETYPE могло автоматически отвечать на вызов по телефонной линии, передайте модему команду ATS0=1 и нажмите клавишу . Поэкспериментируйте с приложением TELETYPE, передавая модему различные команды и наблюдая на экране ответные сообщения.
Рис. 7.4. Приложение TELETYPE
Вы можете передать модему команду набора номера удаленного абонента. Чтобы набрать номер 987-65-43 достаточно ввести команду ATDP 987-65-43 и нажать клавишу .
После установления связи с удаленным модемом вы можете обменяться с ним текстовыми сообщениями. Набирайте передаваемый текст на клавиатуре и просматривайте ответ от удаленного компьютера в главном окне приложения.
Когда вы закончите сеанс связи с удаленным компьютером, переведите модем в командный режим и передайте ему команду ATH0. Модем повесит трубку и отключится от телефонной линии.
Чтобы перевести модем из режима передачи данных в командный режим, подождите 2-3 секунды, наберите на клавиатуре три знака '+' и дождитесь от модема ответа OK. Внешний вид главного окна приложения TELETYPE представлен на рисунке 7.4.
Главный файл приложения TELETYPE приведен в листинге 7.16. После запуска TELETYPE управление получает главная функция приложения WinMain.
Если параметр hPrevInstance функции WinMain равен нулю, WinMain вызывает функцию инициализации приложения InitApp. В противном случае на экран выводится сообщение о невозможности запуска второй копии приложения.
Функция InitApp регистрирует класс окна WMODEM и возвращает управление функции WinMain. На базе зарегистрированного класса окна создается и отображается главное окно приложения, после чего запускается обычный цикл обработки сообщений.
При создании окна в функцию окна передается сообщение WM_CREATE. Получив это сообщение, функция окна вызывает функцию InitTTY, которая определяет различные параметры окна и сохраняет их в глобальных переменных. Затем вызывается функция PostMessage, которая отправляет сообщение WM_CONNECT функции главного окна приложения. Сообщение WM_CONNECT определено во включаемом файле TELETYPE.H следующим образом:
#define WM_CONNECT WM_USER
Константа WM_USER специально предназначена для определения приложениями своих собственных кодов сообщений.
Обработчик сообщения WM_CONNECT вызывает функцию InitCommPort. Эта функция открывает и инициализирует COM-порт в соответствии с данными из файла TELETYPE.INI, разрешает генерацию драйвером COM-порта сообщений WM_COMMNOTIFY и устанавливает сигнала DTR.
После обработки сообщения WM_CONNECT можно набирать на клавиатуре текст. Для получения ввода с клавиатуры наше приложение обрабатывает сообщение WM_CHAR, вызывая функцию UserChat, которая передает код введенного символа драйверу COM-порта и отображает его на экране.
Меню приложения TELETYPE содержит две строки: "Информация" и "Выход". При выборе строки "Информация" на экране отображается короткая информация о приложении.
Когда вы закончите работать с TELETYPE, завершите приложение, выбрав из меню строку "Выход". В этом случае вызывается функция PostMessage, передающая функции окна сообщение WM_CLOSE.
Обработчик сообщения WM_CLOSE вызывает функцию CloseCommPort, которая закрывает COM-порт и завершает приложение.
Листинг 7.16. Файл TELETYPE.CPP
// Определяем константу MAIN_MODULE. Она используется
// в файле TELE.H
#define MAIN_MODULE
#include
#include
#include
#include "tele.h"
// Имя класса главного окна приложения
char szClassName[] = "WMODEM";
// Заголовок главного окна приложения
char szWindowTitle[] = "Телетайп";
//=============================================================
// Функция WinMain
//=============================================================
#pragma argsused
int PASCAL
WinMain( HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpszCmdLine,
int nCmdShow)
{
MSG msg; // структура для работы с сообщениями
HWND hwnd; // идентификатор главного окна приложения
// Проверяем, не запускалось ли это приложение ранее
if(!hPrevInstance)
{
// Сохраняем в глобальной переменной hInst идентификатор
// приложения
hInst = hInstance;
// Если не запускалось, вызываем функцию InitApp
if(!InitApp(hInstance))
return FALSE;
}
else
{
MessageBeep(MB_ICONASTERISK);
BWCCMessageBox( NULL,
"Можно запускать только одну копию приложения",
"Ошибка", MB_OK | MB_ICONSTOP);
return FALSE;
}
// Создаем главное окно приложения
hwnd = CreateWindow(
szClassName,
szWindowTitle,
WS_OVERLAPPED | WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL,
hInstance,
NULL);
// Если создать окно не удалось, завершаем приложение
if(!hwnd)
{
MessageBeep(MB_ICONASTERISK);
BWCCMessageBox( NULL,
"Ошибка при создании главного окна приложения",
"Ошибка", MB_OK | MB_ICONSTOP);
return FALSE;
}
// Рисуем окно
ShowWindow(hwnd, nCmdShow);
// Передаем функции окна сообщение WM_PAINT
UpdateWindow(hwnd);
// Запускаем цикл обработки сообщений
while (GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
// ============================================================
// Функция InitApp
// ============================================================
BOOL
InitApp(HINSTANCE hInstance)
{
ATOM aWndClass; // атом для кода возврата
WNDCLASS wndclass; // структура для регистрации
// класса окна
// Запмсываем нулевые значения во все поля структуры
memset(&wndclass, 0, sizeof(wndclass));
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = (WNDPROC) WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(hInstance, "PHONE");
wndclass.hCursor = LoadCursor( NULL, IDC_ARROW );
wndclass.hbrBackground = GetStockObject( WHITE_BRUSH );
wndclass.lpszMenuName = (LPSTR) "APP_MENU";
wndclass.lpszClassName = (LPSTR) szClassName;
// Регистрируем класс окна szClassName
aWndClass = RegisterClass(&wndclass);
// Возвращаем результат регистрации класса
return(aWndClass != 0);
}
// ============================================================
// Функция окна WndProc
// ============================================================
LRESULT CALLBACK _export
WndProc( HWND hwnd,
UINT message,
WPARAM wParam,
LPARAM lParam )
{
switch( message )
{
case WM_CREATE:
{
// Определяем параметры окна
InitTTY( hwnd );
// Отправляем сообщение WM_CONNECT
PostMessage( hwnd,WM_CONNECT,0,0L );
return 0;
}
case WM_CONNECT:
{
// Открываем и инициализируем COM-порт
idOpenCommPort = InitCommPort(hwnd);
// В случае ошибки отображаем сообщение и завершаем
// приложение
if (idOpenCommPort < 0)
{
MessageBeep(MB_ICONASTERISK);
BWCCMessageBox(hwnd,"COM-порт не открыт","Ошибка",
MB_ICONSTOP | MB_OK);
PostMessage(hwnd,WM_CLOSE,0,0L);
}
return 0;
}
case WM_COMMNOTIFY:
{
// Вызываем обработчик сообщения WM_COMMNOTIFY
ProcessCommNotify( hwnd, wParam, LOWORD( lParam ));
return 0;
}
case WM_SETFOCUS:
{
// Приложение получило фокус ввода
SetFocusTTY(hwnd);
return 0;
}
case WM_KILLFOCUS:
{
// Приложение потеряло фокус ввода
KillFocusTTY(hwnd);
return 0;
}
case WM_CHAR:
{
// Пользователь нажал на клавишу
UserChat(hwnd, message, wParam, lParam);
return 0;
}
case WM_COMMAND:
{
switch ( wParam )
{
case CM_EXIT:
{
PostMessage( hwnd, WM_CLOSE, 0, 0L );
break;
}
case CM_ABOUT:
{
About(hwnd);
break;
}
}
return 0;
}
case WM_CLOSE:
{
// Закрываем COM-порт и завершаем приложение
CloseCommPort(idOpenCommPort);
DestroyWindow( hwnd );
PostQuitMessage( 0 );
return 0;
}
default:
return( DefWindowProc( hwnd, message, wParam, lParam ) );
}
}
// ============================================================
// Функция About
// ============================================================
void About(HWND hwnd)
{
BWCCMessageBox(hwnd,
"Телекоммуникационная программа\n\n"
"(C) Фролов Г.В., 1994",
"Информация",
MB_OK | MB_ICONINFORMATION);
return;
}
В файле CONNECT.CPP (см. листинг 7.17) содержится определение функции UserChat. Эта функция предназначен для обработки сообщения WM_CHAR, поступающего функции окна в ответ на ввод с клавиатуры.
Функция UserChat проверяет, нажал ли пользователь клавишу (). Если нажал, то в рабочий массив szTemp записываются три символа '\r', '\n' и '\0'. Таким образом, в szTemp записывается строка "\r\n". В противном случае в szTemp записывается только код нажатой клавиши, содержащийся в параметре wParam и символ '\0'.
Строка, подготовленная в массиве szTemp, передается в COM-порт и далее модему.
WriteComm( idOpenCommPort,(LPSTR)szTemp,
lstrlen((LPCSTR)szTemp));
После вызова функции WriteComm приложение вызывает функцию GetCommError, которая сбрасывает код ошибки, если таковая случилась при передаче. В нашем приложении полученный код ошибки не обрабатывается.
Переданная модему строка не отображается автоматически в окне приложения. Для этого строка, записанная в szTemp, передается функции WriteTTY, определенной в файле TTY.CPP.
WriteTTY( szTemp, hwnd, DARK_BLUE );
Первый параметр функции WriteTTY должен содержать строку, которую надо отобразить на экране, второй параметр - идентификатор окна и третий - цвет отображаемой строки. Мы указали в качестве третьего параметра константу DARK_BLUE, соответствующую синему цвету. Константы DARK_BLUE и DARK_GREEN, используемые в нашем приложении определены в файле TELETYPE.H.
Листинг 7.17. Файл CONNECT.CPP
#include
#include "tele.h"
// ============================================================
// Функция UserChat
// ============================================================
#pragma argsused
LRESULT
UserChat(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
char szTemp[3]; // Рабочий массив
switch (wParam)
{
// Пользователь нажал клавишу ()
case VK_RETURN:
szTemp[0] = '\r';
szTemp[1] = '\n';
szTemp[2] = '\0';
break;
// Пользователь нажал другую клавишу (не )
default:
szTemp[0] = (char)wParam;
szTemp[1] = '\0';
break;
}
// Передаем код нажатой клавиши в COM-порт
WriteComm( idOpenCommPort,(LPSTR)szTemp,
lstrlen((LPCSTR) szTemp));
// Получаем и сбрасываем код ошибки
GetCommError( idOpenCommPort,NULL );
// Отображаем символ нажатой клавиши на экране
WriteTTY( szTemp, hwnd, DARK_BLUE );
return 0;
}
В файле COMMPORT.CPP (см. листинг 7.18) определены основные функции, непосредственно взаимодействующие с COM-портом - InitComPort, CloseComPort и ReadCommPort.
Функция InitComPort выполняет все действия по инициализации COM-порта. Рассмотрим ее более подробно.
Сначала InitComPort определяет режим работы COM-порта, для этого она считывает строку Mode из раздела Port файла TELETYPE.INI. Затем функция OpenComm открывает соответствующий COM-порт.
Потом функция BuildCommDCB заполняет структуру DCB, которая передается функции SetCommState. Функция SetCommState устанавливает новый режим работы порта. Сразу после открытия COM-порта в его буферах могут остаться данные. Чтобы их удалить, мы использовали функцию FlushComm.
В приложении TELETYPE мы обрабатываем сообщения WM_COMMNOTIFY, вырабатываемые драйвером COM-порта. Для того чтобы разрешить генерацию этих сообщений, предназначена функция EnableCommNotification.
if(!EnableCommNotification(idCommPort,hwnd,32,-1))
return -1;
После вызова EnableCommNotification функции окна будут поступать сообщения WM_COMMNOTIFY с кодом извещения CN_RECEIVE, если во входную очередь COM-порта поступило больше 32 символов или истек тайм-аут. Более подробную информацию о функции EnableCommNotification можно получить в разделах "Функция EnableCommNotification" и "Сообщение WM_COMMNOTIFY".
Затем вызывается функция EscapeCommFunction, которая устанавливает сигнал DTR, сообщая модему, что компьютер готов к обмену данными.
На этом функция InitCommPort заканчивает свою работу и возвращает вызывающей процедуре идентификатор открытого COM-порта.
Самая простая функция нашего приложения, предназначенная для работы с COM-портами, называется CloseComPort. Она сбрасывает сигнал DTR и закрывает COM-порт.
В файле COMMPORT.CPP также определена функция ReadComPort, предназначенная для чтения данных из выходной очереди COM-порта.
Функция имеет три параметра. Первый параметр idComDev определяет идентификатор COM-порта, из которого будут прочитаны данные. Второй параметр szDest должен содержать адрес буфера, в который будет записана поступающая информация, а последний параметр nLength указывает размер этого буфера.
Функция ReadCommPort содержит внутри себя цикл, в котором происходит чтение данных из выходной очереди COM-порта. Мы выполняем чтение из входной очереди в цикле, так как при больших скоростях передачи информации (больше 9600 бит/с) за время чтения данных из очереди, в нее могут поступить новые данные.
В цикле сначала вызывается функция GetCommError. Она заполняет структуру ComStat типа COMSTAT. Нас интересует только поле cbInQue этой структуры. В нем записано, сколько байт находится во входной очереди COM-порта. Если в очереди есть данные, считываем их, вызывая функцию ReadComm. В противном случае выходим из цикла чтения и возвращаем вызывающей процедуре количество прочитанных байт.
Листинг 7.18. Файл COMMPORT.CPP
#include
#include "tele.h"
// ============================================================
// Функция InitComPort
// ============================================================
int InitCommPort(HWND hwnd)
{
DCB dcb; // структура DCB
int idCommPort; // идентификатор COM-порта
char szPort[6]; // имя порта
char szInitMode[40]; // режим работы
int nResult; // временная переменная
// Определяем режим работы COM-порта, для этого считываем
// строку Mode из раздела Port файла phone.ini
GetPrivateProfileString("Port", "Mode", "COM2:2400,n,8,1",
szInitMode, sizeof(szInitMode), "teletype.ini");
// Открываем COM-порт, заданный в строке szInitMode
wsprintf( szPort, "COM%c", szInitMode[3] );
idCommPort = OpenComm(szPort, INQUEUE, OUTQUEUE);
if (idCommPort < 0)
return idCommPort;
// Заполняем структуру DCB в соответствии с szInitMode
nResult = BuildCommDCB(szInitMode,&dcb);
if (nResult < 0)
return nResult;
// Устанавливаем новый режим работы COM-порта
nResult = SetCommState(&dcb);
if (nResult < 0)
return nResult;
// Удаляем данные из входной и выходной очередей COM-порта
FlushComm(idCommPort, 1);
FlushComm(idCommPort, 0);
// Разрешаем генерацию сообщения WM_COMMNOTIFY
if(!EnableCommNotification(idCommPort,hwnd,32,-1))
return -1;
// Подаем сигнал DTR
EscapeCommFunction(idCommPort, SETDTR);
return idCommPort;
}
// ============================================================
// Функция CloseComPort
// ============================================================
int CloseCommPort(int idCommPort)
{
// Сбрасываем сигнал DTR
EscapeCommFunction(idCommPort, CLRDTR);
// Закрываем COM-порт
CloseComm(idCommPort);
return 0;
}
// ============================================================
// Функция ReadComPort
// ============================================================
int ReadCommPort(int idComDev, LPSTR szDest, int nLength)
{
COMSTAT ComStat;
int nTotalRead = 0, nRead = 0;
// Цикл чтения данных из входной очереди COM-порта
while(nLength > nTotalRead)
{
// Определяем, есть ли данные во входной очереди
GetCommError(idComDev,&ComStat);
if (ComStat.cbInQue == 0)
break;
// Считываем данные из входной очереди COM-порта
nRead = ReadComm(idComDev,&(szDest[nTotalRead]),
nLength - nTotalRead);
// Возникла ошибка
if(nRead < 0)
{
nTotalRead = -nTotalRead - nRead;
break;
}
nTotalRead += nRead;
}
return nTotalRead;
}
Файл COMMSG.CPP (см. листинг 7.19) содержит определение только одной функции ProcessCommNotify. Эта функция выполняет обработку сообщений WM_COMMNOTIFY, вырабатываемых COM-портом.
При получении сообщения WM_COMMNOTIFY с кодом извещения CN_RECEIVE функция ProcessCommNotify считывает данные из входной очереди COM-порта, вызывая функцию ReadCommPort.
Если ReadCommPort возвратила положительную величину, отображаем на экране прочитанную информацию при помощи функции WriteTTY. Обратите внимание, что данные полученные из COM-порта, отображаются зеленым цветом, в то время как данные передаваемые в COM-порт - синим.
Если функция ReadCommPort возвратила отрицательную величину, значит при чтении из COM-порта возникла ошибка, например, был получен байт с ошибкой по четности. В этом случае мы также отображаем полученные данные на экране, а затем вызываем функцию GetCommError, которая определяет код ошибки и сбрасывает регистр ошибок.
Листинг 7.19. Файл COMMSG.CPP
#include
#include "tele.h"
// ============================================================
// Функция ProcessCommNotify
// ============================================================
int ProcessCommNotify(HWND hwnd, int nComID, int nNotification)
{
int nResult; // временная переменная
char szData[OUTQUEUE+1]; // временный буфер данных
// Получено сообщение WM_COMMNOTIFY с кодом извещения
// CN_RECEIVE
if ( nNotification & CN_RECEIVE )
{
// Считываем данные из входной очереди COM-порта
nResult = ReadCommPort ( nComID, (LPSTR)szData, OUTQUEUE );
if (nResult > 0)
{
szData[nResult] = 0;
// Отображаем считанные из COM-порта данные в окне
WriteTTY(szData, hwnd, DARK_GREEN);
}
// Возникла ошибка при чтении из COM-порта
else if (nResult < 0)
{
szData[-nResult] = 0;
// Отображаем считанные из COM-порта данные в окне
// и возвращаем код ошибки
WriteTTY(szData, hwnd, DARK_GREEN);
return nResult;
}
// Определяем код ошибки и сбрасываем регистр ошибок
GetCommError(nComID,NULL);
}
return nResult;
}
Предусмотрены четыре функции, предназначенные для работы с главным окном приложения TELETYPE. Их имена - InitTTY, WriteTTY, SetFocusTTY, KillFocusTTY. Эти функции определены в файле TTY.CPP (см. листинг 7.20). Так как указанные функции не содержат ничего, непосредственно относящегося к COM-портам, мы опишем их кратко.
Функция InitTTY определяет размеры окна приложения в символах. Главное окно приложения не может изменять свой размер. Поэтому функцию InitTTY достаточно вызвать в начале работы приложения. Прототип функции представлен ниже:
void InitTTY(HWND hwnd);
Функция InitTTY имеет только один параметр hwnd, который должен содержать идентификатор главного окна приложения. Это значение возвращается функцией CreateWindow.
Функция WriteTTY предназначена для отображения данных в главном окне приложения. Прототип функции:
void WriteTTY(LPSTR lpOutString, HWND hwnd,
COLORREF rgbColor);
Первый параметр lpOutString должен содержать указатель на строку символов, закрытую двоичным нулем. Эта строка будет выведена на экран.
Второй параметр hwnd должен содержать идентификатор главного окна приложения.
Последний параметр функции WriteTTY - rgbColor. Он определяет цвет символов, которые будут отображаться на экране. В приложении TELETYPE символы, полученные от модема, отображаются зеленым цветом, а символы набираемые пользователем на клавиатуре и передаваемые модему - синим.
Функции SetFocusTTY и KillFocusTTY управляют текстовым курсором, отображаемым в окне приложения. Функция SetFocusTTY отображает курсор, а функция KillFocusTTY - убирает его из окна приложения. Прототипы функций SetFocusTTY и KillFocusTTY аналогичны:
void SetFocusTTY(HWND hwnd);
void KillFocusTTY(HWND hwnd);
Эти функции имеют единственный параметр hwnd, который должен содержать идентификатор главного окна приложения.
Листинг 7.20. Файл TTY.CPP
#include
#include "tele.h"
// Текущее положение курсора
static int nyCurrRow, nxCurrCol;
// Размер главного окна приложения в символах
static int nyRows, nxCols;
// Размер символов
static int nxCharSize, nyCharSize;
// ============================================================
// Функция InitTTY
// ============================================================
void InitTTY(HWND hwnd)
{
HDC hdc; // индекс контекста устройства
TEXTMETRIC tiTextMetric; // структура для записи метрик
// шрифта
RECT rcTTYWindow; // размер окна
// Определяем начальное положение курсора
nyCurrRow = 0;
nxCurrCol = 0;
// Получаем контекст отображения
hdc = GetDC( hwnd );
// Выбираем в контекст отображения шрифт OEM_FIXED_FONT
SelectObject(hdc, GetStockObject(OEM_FIXED_FONT));
// Заполняем структуру tiTextMetric информацией о метрике
// шрифта, выбранного в контекст отображения
GetTextMetrics(hdc, &tiTextMetric);
// Освобождаем контекст
ReleaseDC( hwnd, hdc );
// Запоминаем среднее значение ширины символов
nxCharSize = tiTextMetric.tmAveCharWidth;
// Запоминаем значение высоты символов с учетом
// межстрочного интервала
nyCharSize = tiTextMetric.tmHeight +
tiTextMetric.tmExternalLeading;
// Определяем текущее размеры окна
GetClientRect(hwnd, &rcTTYWindow);
// Определяем количество строк, помещающихся в окне
nyRows = (rcTTYWindow.bottom - rcTTYWindow.top) / nyCharSize;
// Определяем количество символов, помещающихся в строке окне
nxCols = (rcTTYWindow.right - rcTTYWindow.left) / nxCharSize;
return;
}
// ============================================================
// Функция WriteTTY
// ============================================================
void
WriteTTY( LPSTR lpOutString, HWND hwnd, COLORREF rgbColor )
{
LPSTR lpCurrChar; // рабочий указатель
HDC hdc; // индекс контекста устройства
// Получаем контекст отображения
hdc = GetDC( hwnd );
// Выбираем в контекст отображения шрифт OEM_FIXED_FONT
SelectObject( hdc, GetStockObject(OEM_FIXED_FONT));
// Устанавливаем цвет текста
SetTextColor(hdc, rgbColor);
// Устанавливаем цвет фона текста соответствующий цвету окна
SetBkColor(hdc, GetSysColor(COLOR_WINDOW));
// Устанавливаем режим вывода текста
SetBkMode(hdc, OPAQUE);
// Выключаем курсор
HideCaret( hwnd );
// Отображаем строку lpOutString в окне
for ( lpCurrChar = lpOutString; *lpCurrChar; lpCurrChar++ )
{
switch ( *lpCurrChar )
{
// Возвращаем курсор на одну позицию назад
case ASCII_BACK:
{
if(nxCurrCol)
nxCurrCol--;
break;
}
// Переводим курсор в начало текущей строки
case ASCII_CR:
{
nxCurrCol = 0;
break;
}
// Переводим курсор на новую строку
case ASCII_LF:
{
nyCurrRow++;
// При необходимости выполняем вертикальную
// свертку окна
if ( nyCurrRow == ( nyRows - 1 ) )
{
ValidateRect( hwnd, NULL );
ScrollWindow( hwnd, 0, -nyCharSize, NULL, NULL );
// Передаем сообщение WM_PAINT
UpdateWindow( hwnd );
// Изменяем текущее положение курсора
nyCurrRow = nyRows - 2;
}
break;
}
// Подаем звуковой сигнал
case ASCII_BELL:
{
MessageBeep(MB_OK);
break;
}
default:
{
// Отображаем очередной символ из строки
// lpOutString в текущей позиции окна
TextOut( hdc, nxCurrCol * nxCharSize,
nyCurrRow * nyCharSize, lpCurrChar, 1 );
// Смещаем курсор вправо
nxCurrCol++;
// При необходимости переходим на следующую строку
if ( nxCurrCol == ( nxCols - 1 ) )
{
nxCurrCol = 0;
nyCurrRow++;
// Если это необходимо, выполняем вертикальную
// свертку экрана
if ( nyCurrRow == ( nyRows - 1 ) )
{
ValidateRect( hwnd, NULL );
ScrollWindow( hwnd, 0, -nyCharSize, NULL, NULL );
UpdateWindow( hwnd );
nyCurrRow = nyRows - 2;
}
}
break;
}
}
}
// Перемещаем курсор в новую позиицию
SetCaretPos( nxCurrCol * nxCharSize,
nyCurrRow * nyCharSize );
// Отображаем курсор
ShowCaret( hwnd );
// Освобождаем контекст
ReleaseDC( hwnd, hdc );
return;
}
// ============================================================
// Функция SetFocusTTY
// ============================================================
void SetFocusTTY(HWND hwnd)
{
// Создаем текстовый курсор
CreateCaret( hwnd, NULL, nxCharSize, nyCharSize );
// Перемещаем курсор в текущую позиицию
SetCaretPos( nxCurrCol * nxCharSize,
nyCurrRow * nyCharSize );
// Отображаем курсор
ShowCaret( hwnd );
return;
}
// ============================================================
// Функция KillFocusTTY
// ============================================================
void KillFocusTTY(HWND hwnd)
{
// Выключаем курсор
HideCaret(hwnd);
// Удаляем курсор
DestroyCaret();
return;
}
Включаемый файл TELE.H (см. листинг 7.21) содержит определения констант, идентификаторов и глобальных переменных, а также объявления функций, используемых в приложении.
Листинг 7.21. Файл TELE.H
#define DARK_BLUE RGB(0,0,127) // синий цвет
#define DARK_GREEN RGB(0,127,0) // зеленый цвет
// Идентификаторы меню приложения
#define CM_EXIT 101
#define CM_ABOUT 102
// Сообщение WM_CONNECT
#define WM_CONNECT WM_USER
// Размеры входной и выходной очереди
#define INQUEUE 4096
#define OUTQUEUE 4096
// ASCII-коды
#define ASCII_BELL 0x07
#define ASCII_BACK 0x08
#define ASCII_LF 0x0A
#define ASCII_CR 0x0D
// Определяем глобальные переменные только в главном модуле
#ifdef MAIN_MODULE
HINSTANCE hInst;
int idOpenCommPort;
#else
// Объявляем глобальные переменные,
// определенные в главном модуле
extern int idOpenCommPort;
extern HINSTANCE hInst;
#endif
// Функции, определенные в файле TELETYPE.CPP
void About(HWND hwnd);
BOOL InitApp(HINSTANCE hInstance);
LRESULT CALLBACK _export
WndProc (HWND hWnd, UINT message, WPARAM wParam,
LPARAM lParam);
// Функция, определенная в файле COMMSG.CPP
LRESULT
UserChat(HWND hWnd, UINT message,
WPARAM wParam, LPARAM lParam);
// Функция, определенная в файле COMMSG.CPP
int
ProcessCommNotify(HWND hWnd, int nComID, int nNotification);
// Функции, определенные в файле COMMPORT.CPP
int InitCommPort( HWND hwnd);
int CloseCommPort( int nComID );
int ReadCommPort( int nComID , LPSTR Data , int nMaxLength );
// Функции, определенные в файле TTY.CPP
void InitTTY(HWND hWnd);
void WriteTTY(LPSTR lpOutString, HWND hwnd, COLORREF rgbColor);
void SetFocusTTY(HWND hwnd);
void KillFocusTTY(HWND hwnd);
В листинге 7.22 представлен исходный текст файла TELETYPE.RC, содержащего описание ресурсов приложения TELETYPE. В нем описаны меню APP_MENU и пиктограмма PHONE.
Листинг 7.22. Файл TELETYPE.RC
#include "tele.h"
PHONE ICON "teletype.ico"
APP_MENU MENU
BEGIN
MENUITEM "Выход", CM_EXIT
MENUITEM "Информация", CM_ABOUT
END
В листинге 7.23 приведено изображение пиктограммы, расположенной в файле TELETYPE.ICO, на который ссылается оператор ICON в файле описания ресурсов TELETYPE.RC.
Листинг 7.23. Файл TELETYPE.ICO
Файл определения модуля для приложения TELETYPE приведен в листинге 7.24.
Листинг 7.24. Файл TELETYPE.DEF
; ==========================================================
; Файл определения модуля
; ==========================================================
NAME TELETYPE
DESCRIPTION 'Приложение TELETYPE, (C) 1994, Frolov G.V.'
EXETYPE windows
STUB 'winstub.exe'
STACKSIZE 16384
HEAPSIZE 16384
CODE preload moveable discardable
DATA preload moveable multiple
|