Программирование для IBM OS/2© Александр Фролов, Григорий ФроловТом 25, М.: Диалог-МИФИ, 1993, 286 стр. 1.3. Приложение MYWINDOWТеперь, когда вы познакомились с основными понятиями и структурой приложения Presentation Manager, можно перейти к практике. Для начала мы создадим простейшее приложение MYWINDOW, которое создает одно главное окно и не имеет ни меню, ни других атрибутов современных приложений. Если вы установите курсор мыши в область Client Window и сделаете щелчок правой либо левой клавишей мыши, на экране появится диалоговая панель, в которой будут показаны координаты курсора мыши (в системе координат, связанной с окном Client Window). Внешний вид приложения MYWINDOW и диалоговая панель с координатами курсора мыши показана на рис. 1.2.
Рис. 1.2. Главное окно приложения MYWINDOW Исходные тексты приложения приведены в листинге 1.1. Листинг 1.1. Файл mywindow\mywindow.c
// ===================================================
// Определения
// ===================================================
// Определения для файла os2.h
#define INCL_WIN
#define INCL_GPI
#define INCL_WINDIALOGS
// Файл содержит определения, необходимые
// для любого приложения OS/2
#include <os2.h>
// Этот файл нужен для определения функции sprintf
#include <stdio.h>
// Определение констант для приложения MYWINDOW
#include "mywindow.h"
// Прототип функции окна приложения
MRESULT EXPENTRY WndProc(HWND, ULONG, MPARAM, MPARAM);
// ===================================================
// Глобальные переменные
// ===================================================
// Идентификатор Anchor-block
HAB hab;
// Идентификатор окна Frame Window - главного окна приложения
HWND hWndFrame;
// Идентификатор окна Client Window
HWND hWndClient;
// Заголовок приложения
CHAR szAppTitle[] = "My First OS/2 Application";
// ===================================================
// Главная функция приложения main
// Получает управление при запуске приложения
// ===================================================
int main ()
{
// Идентификатор очереди сообщений
HMQ hmq;
// Структура, в которую записывается сообщение,
// извлеченное из очереди
QMSG qmsg;
// Переменная для хранения кода возврата
BOOL fRc;
// Флаги для создания окна Frame Window
ULONG flFrameFlags =
FCF_SYSMENU | FCF_TITLEBAR | FCF_MINMAX |
FCF_SIZEBORDER | FCF_SHELLPOSITION | FCF_TASKLIST |
FCF_ICON;
// Имя класса главного окна
CHAR szWndClass[] = "MYWINDOW";
// Инициализация приложения, необходимая для
// использования функций Presentation Manager
hab = WinInitialize (0);
// При невозможности инициализации выводим
// сообщение об ошибке
if(hab == NULLHANDLE)
{
WinMessageBox (HWND_DESKTOP, HWND_DESKTOP,
"Ошибка инициализации",
"Ошибка", 0, MB_ICONHAND | MB_OK);
return(-1);
}
// Создаем очередь сообщений
hmq = WinCreateMsgQueue (hab, 0);
if(hmq == NULLHANDLE)
{
WinMessageBox (HWND_DESKTOP, HWND_DESKTOP,
"Ошибка при создании очереди сообщений",
"Ошибка", 0, MB_ICONHAND | MB_OK);
// Перед аварийным завершением приложения
// необходимо вызвать функцию WinTerminate
WinTerminate (hab);
return(-1);
}
// Регистрация главного окна приложения
fRc = WinRegisterClass (hab, szWndClass,
(PFNWP)WndProc, 0, 0);
if(fRc == FALSE)
{
WinMessageBox (HWND_DESKTOP, HWND_DESKTOP,
"Ошибка при регистрации класса главного окна",
"Ошибка", 0, MB_ICONHAND | MB_OK);
// Перед аварийным завершением приложения
// уничтожаем созданную ранее очередь сообщений и
// вызываем функцию WinTerminate
WinDestroyMsgQueue (hmq);
WinTerminate (hab);
return(-1);
}
// Создаем главное окно приложения
hWndFrame = WinCreateStdWindow (HWND_DESKTOP,
WS_VISIBLE ,
&flFrameFlags, szWndClass, szAppTitle,
0, 0, ID_APP_FRAMEWND, &hWndClient);
if(hWndFrame == NULLHANDLE)
{
WinMessageBox (HWND_DESKTOP, HWND_DESKTOP,
"Ошибка при создании главного окна",
"Ошибка", 0, MB_ICONHAND | MB_OK);
WinDestroyMsgQueue (hmq);
WinTerminate (hab);
return(-1);
}
// Запускаем цикл обработки сообщений
while(WinGetMsg (hab, &qmsg, 0, 0, 0))
WinDispatchMsg (hab, &qmsg);
// Уничтожаем главное окно приложения
WinDestroyWindow(hWndFrame);
// Удаляем очередь сообщений и вызываем
// функцию WinTerminate
WinDestroyMsgQueue (hmq);
WinTerminate (hab);
// Возвращаем управление операционной системе
return(0);
}
// ===================================================
// Функция главного окна приложения
// Обрабатывает сообщения, поступающие
// в очередь приложения
// ===================================================
MRESULT EXPENTRY
WndProc(HWND hWnd, ULONG msg, MPARAM mp1, MPARAM mp2)
{
// Временный буфер для подготовки сообщения
CHAR szMsg[100];
switch (msg)
{
// Обработчик сообщения WM_ERASEBACKGROUND
// Это сообщение поступает перед перерисовкой
// внутренней области окна
case WM_ERASEBACKGROUND :
return(MRFROMLONG(1L));
// Обработчик сообщений
// WM_BUTTON1DOWN и WM_BUTTON2DOWN .
// Эти сообщения поступают, когда пользователь
// делает в окне приложения щелчок левой и правой
// кнопкой мыши, соответственно
case WM_BUTTON1DOWN :
case WM_BUTTON2DOWN :
{
// Определяем координаты курсора мыши и
// записываем их в виде текстовой строки
// во временный буфер
sprintf (szMsg, "(x, y) = (%ld, %ld)",
SHORT1FROMMP (mp1), SHORT2FROMMP (mp1));
// Отображаем содержимое временного буфера
// на экране
WinMessageBox (HWND_DESKTOP, hWnd, szMsg,
"Координаты курсора мыши", 0,
MB_INFORMATION | MB_OK);
}
// Если наша функция окна не обрабатывает
// сообщение, его следует обязательно передать
// функции WinDefWindowProc
default:
return(WinDefWindowProc (hWnd, msg, mp1, mp2));
}
}
Файл mywindow.h, который содержит определение идентификатора окна Frame Window , представлен в листинге 1.2. Листинг 1.2. Файл mywindow\mywindow.h #define ID_APP_FRAMEWND 1 Определения и глобальные переменныеИсходные тексты любого приложения IBM OS/2 должны включать в себя файл os2.h, в котором определяются константы, типы данных и функции программного интерфейса операционной системы. Так как этот интерфейс очень обширный, для сокращения времени трансляции в файле os2.h используются операторы условного включения других include-файлов (которые, в свою очередь, также могут включать в себя файлы определений при помощи оператора #include). Изучите файл os2.h самостоятельно, обратив внимание на то, какие именно файлы включаются при определении макро INCL_WIN , INCL_GPI и INCL_WINDIALOGS , использованных в нашем приложении. Вы сможете найти этот файл в каталоге INCLUDE вашей системы разработки. Так как в нашем приложении мы пользуемся станадртной функцией sprintf , мы включили файл определений stdio.h . Отметим, что в приложениях Presentation Manager нельзя использовать функции стандартного ввода/вывода в файлы и потоки, однако функция sprintf к таковым не относится - она используется в нашем приложении для формирования текстовой строки. Далее в исходный текст нашего приложения включается файл mywindow.h, который содержит определение константы ID_APP_FRAMEWND (идентификатор окна Frame Window ). Этот идентификатор необходим для создания главного окна приложения. Кроме того, мы определяем прототип функции окна WndProc: MRESULT EXPENTRY WndProc(HWND, ULONG, MPARAM, MPARAM); Эта функция обрабатывает сообщения, поступающие в окно Client Window нашего приложения. Далее в исходном тексте определены несколько глобальных переменных. Переменная hab служит для хранения идентификатора Anchor-block, полученного при регистрации основной задачи нашего приложения в системе Presentation Manager функцией WinInitialize . В переменных hWndFrame и hWndClient хранятся, соответственно, идентификаторы окон Frame Window и Client Window . Строка szAppTitle содержит заголовок приложения, который отображается в верхней части главного окна. Функция mainФункция main , получающая управление при запуске приложения, создает главное окно и организует цикл обработки сообщений. В переменной hmq хранится идентификатор очереди сообщений, созданной функцией WinCreateMsgQueue . При выборке сообщение записвается для временного хранения в структуру qmsg. Переменная flFrameFlags хранит набор флагов, которые используются при создании окна Frame Window : ULONG flFrameFlags = FCF_SYSMENU | FCF_TITLEBAR | FCF_MINMAX | FCF_SIZEBORDER | FCF_SHELLPOSITION | FCF_TASKLIST | FCF_ICON; Мы создаем окно, которое имеет меню, заголовок, кнопки минимизации и максимизации, рамку для изменения размеров окна и пиктограмму. Начальные размеры и расположение окна выбираются оболочкой Workplace Shell. В строке szWndClass записано имя класса, которое используется для окна Client Window . Первое, что делает функция main после запуска приложения - это инициализация при помощи функции WinInitialize . Данная функция должна вернуть идентификатор блока Anchor-block при нормальном завершении и значение NULLHANDLE в случае возникновения ошибки. Как обработать ошибку? Можно, конечно, просто завершить работу приложения, однако в этом случае мы так и не узнаем, что же произошло. Гораздо лучше было бы вывести на экран сообщение об ошибке, однако в приложении Presentation Manager мы не можем воспользоваться для этого такими функциями, как puts или printf . Нас выручит тот факт, что в программном интерфейсе Presentation Manager имеется простейшее средство отображения сообщений в диалоговых панелях - функция WinMessageBox . Вот как мы использовали эту функцию для вывода сообщения при возникновении ошибки инициализации:
if(hab == NULLHANDLE)
{
WinMessageBox (HWND_DESKTOP, HWND_DESKTOP,
"Ошибка инициализации",
"Ошибка", 0, MB_ICONHAND | MB_OK);
return(-1);
}
Прототип функции WinMessageBox приведен ниже:
ULONG WinMessageBox (
HWND hwndParent, // идентификатор родительского окна
HWND hwndOwner, // идентификатор окна владельца
PSZ pszText, // текст сообщения
PSZ pszCaption, // заголовок диалоговой панели
ULONG idWindow, // идентификатор окна
// диалоговой панели
ULONG flStyle); // стиль диалоговой
// панели с сообщением
Позже, когда мы займемся диалоговыми панелями, мы расскажем об этой функции подробнее. Сейчас же достаточно сказать, что через третий параметр этой функции нужно передать адрес строки с сообщением, через четвертый - заголовок окна. Последий параметр определяет внешний вид диалоговой панели (количество и названия кнопок, пиктограмму, отображаемую в левой части диалоговой панели), В случае успешной инициализации наше приложение создает очередь сообщений, вызывая для этого функцию WinCreateMsgQueue . Если функция создания очереди выполнилась с ошибкой, на экран выводится соответствующее сообщение при помощи все той же функции WinMessageBox . Вслед за этим мы вызываем функцию WinTerminate (так как инициализация приложения была выполнена успешно). На следующем этапе функцией WinRegisterClass выполняется регистрация класса для окна Client Window . В качестве второго параметра этой функции мы указываем строку szWndClass, а в качестве третьего - имя функции окна WndProc. После успешной регистрации приложение создает свое главное окно, вызывая функцию WinCreateStdWindow , и запускает цикл обработки сообщений. Детали мы описали ранее, поэтому не будем повторяться. После того как из очереди сообщений будет выбрано сообщение WM_QUIT , функция WinGetMsg вернет значение FALSE и цикл обработки сообщений завершит свою работу. После этого функция main уничтожит главное окно приложения. Затем она удалит очередь сообщений и освободит ресурсы, полученные у системы Presentation Manager, вызвав функцию WinTerminate . На этом приложение завершит свою работу. Функция окнаФункция WndProc обрабатывает сообщения, поступающие в окно Client Window нашего приложения. Эта функция анализирует код сообщения, передаваемый ей через параметр msg и выполняет соответствующие действия. Наше приложение очень простое, поэтому и действий немного. Сообщение WM_ERASEBACKGROUND посылается в окно для того, чтобы функция окна подтвердила необходимость стирания фона окна Client Window . В зависимости от значения, которое возвратит функция окна в ответ на это сообщение, система Presentation Manager будет принимать решение о необходимости стирания. Если функция вренет значение 1, стирание следует выполнить, если 0 - нет. Экспериментируя с приложением, вы можете попробовать изменить возвращаемое значение на нулевое. Вы увидите, что при перемещении окно приложения Client Window не будет перерисовываться. Более детально процесс рисования в окне мы рассмотрим позже. А сейчас займемся сообщениями WM_BUTTON1DOWN и WM_BUTTON2DOWN . Эти сообщения посылаются в функцию окна, когда пользователь, поместив курсор мыши в область окна Client Window , сделает щелчок, соответственно, левой или правой клавишей мыши. Вместе с сообщениями WM_BUTTON1DOWN и WM_BUTTON2DOWN передаются параметры - текущие координаты курсора мыши. Координата по оси X передается через параметр mp1, а координата по оси Y - через параметр mp2. Для того чтобы отобразить эти координаты на экране, мы с помощью функции sprintf подготавливаем текстовую строку и вызываем функцию WinMessageBox . Обратите внимание, что в качестве второго параметра мы указываем идентификатор окна hWnd, который передается в функцию окна через первый параметр. Следует отметить, что вопрос использования координатных систем и координат не так прост, как может показаться на первый взгляд. Забегая вперед, скажем что вы можете выбирать одну из нескольких систем координат и изменять направление координатных осей. Об этом мы расскажем позже. Все остальные сообщения, попадающие в функцию окна, мы передаем функции WinDefWindowProc . Это необходимо не только для правильной работы нашего приложения, но и для нормальной работы системы Presentation Manager. Блокируя обработку сообщений, мы можем привести операционную систему в такое состояние, когда потребуется ее перезапуск. Обратите также внимание на то, что в нашей функции окна мы не обрабатываем сообщения WM_CREATE и WM_DESTROY , поступающие, соответственно, при создании и уничтожении окна. Так как наше приложение не выполняет в этих случаях никаких дополнительных действий, мы передаем сообщения WM_CREATE и WM_DESTROY функции WinDefWindowProc . Ресурсы приложенияСоздавая программы для операционной системы MS-DOS , вы получали загрузочный модуль программы из исходного текста, объектных модулей и библиотек объектных модулей. Технология была очень простой - транслятор выполнял преобразования файлов исходного текста в файлы объектных модулей, а редактор связей собирал все объектные модули и библиотеки объектных модулей в исполнимый exe-файл. Полученный исполнимый файл содержал в себе все необходимое для работы (за исключением кода функций операционной системы MS-DOS и системы базового ввода/вывода BIOS , который вызывается через программные прерывания). В мультизадачной среде этот подход приводит к неэкономному расходованию оперативной памяти, поэтому вместо статического редактирования используется динамическое. Основные принципы динамического редактирования в среде IBM OS/2 мы описали в 20 томе "Библиотеки системного программиста". Дополнительно загрузочные файлы приложений IBM OS/2 (также, как и загрузочные файлы приложений Microsoft Windows), содержат данные, которые называются ресурсами приложений. Ресурсы описываются в файле определения ресурсов, который на этапе сборки загрузочного модуля приложения преобразуется в двоичный вид компилятором ресурсов и затем добавляется редактором связей к загрузочному модулю. Наше приложение использует только один ресурс - пиктограмму . Этот ресурс описан в файле определения ресурсов mywindow.rc, который представлен в листинге 1.3 Листинг 1.3. Файл mywindow\mywindow.rc #include <os2.h> #include "mywindow.h" ICON ID_APP_FRAMEWND MYWINDOW.ICO Файл ресурсов не является программой, составленной на языке программирования C, несмотря на то что в него можно включать файлы определений с помощью команды #include. Это обычный текстовый файл, содержащий специальные команды, которые мы подробно рассмотрим позже. Сейчас только заметим, что файл mywindow.h включен для определения константы ID_APP_FRAMEWND - идентификатора окна Frame Window . Этот идентификатор в данном случае используется для ссылки на пиктограмму MYWINDOW.ICO. Пиктограмма MYWINDOW.ICO была нарисована с использованием редактора пиктограмм ICONEDIT , входящего в комплект операционной системы IBM OS/2 Warp и есть на дискете, которую вы можете купить вместе с книгой (на этой дискете есть исходные тексты всех программ, приведенных в нашей книге). Файл определения модуляЕще один файл, который используется редактором связей при создании загрузочного модуля приложения IBM OS/2 и который не имеет аналога в операционной системе MS-DOS, называется файлом определения модуля . В этом файле указывается имя загрузочного модуля, строка описания, режим работы приложения, размер областей памяти, выделенных для кучи и стека, а также перечисляются имена функций обратного вызова. Файл определения модуля mywindow.def для нашего приложения представлен в листинге 1.4. Листинг 1.4. Файл mywindow\mywindow.def NAME MYWINDOW WINDOWAPI DESCRIPTION 'MyWindow Application (C) Frolov A., 1996' HEAPSIZE 4096 STACKSIZE 32768 EXPORTS WndProc В строке описания имени приложения есть параметр WINDOWAPI , который означает, что данное приложение является приложением Presentation Manager. Описание, которое приведено в строке DESCRIPTION , попадает в файл загрузочного модуля приложения и может быть использовано для указания авторских прав или любой другой произвольной информации о приложении. С помощью строки EXPORTS имя функции WndProc попадает в таблицу экспортируемых имен. Обычно такое описание используется для функций, экспортируемых библиотеками динамической загрузки. Трансляция исходных текстов приложенияЕсли вы будете транслировать исходные тексты приложений, приведенных в нашей книге с помощью системы разработки Borland C++ for OS/2, то сможете воспользоваться навыками, полученными при программировании в среде MS-DOS или Microsoft Windows. Все, что вам нужно сделать, это создать новый проект, добавив в него файлы с именами *.c, *.rc и *.def. Затем вы можете транслировать и собирать проект обычным образом. К сожалению, из-за недостатка места в нашей книге мы не сможем описать в деталях этот процесс, однако он не сложен и мы уверены, что вы справитесь с этим сами, точно также, как и с установкой среды разработки Borland C++ for OS/2. Что же касается системы разработки IBM VisualAge C++, то мы подготовили mak-файл, с помощью которого вы сможете собрать проект из командного приглашения OS/2. Этот способ хотя и не слишком удобен по сравнению с использованием интегрированных систем разработки, позволит вам работать с компьютером, оснащенным небольшим объемом оперативной памяти или медленным процессором. Файл называется mywindow.mak (листинг 1.5). Для трансляции приложения с помощью этого файла вы должны сделать каталог, содержащий исходные тексты приложения, текущим и затем запустить программу nmake, указав ей в качестве параметра имя файла mywindow.mak: c:\os2prg\src\mywindow>nmake mywindow.mak Листинг 1.5. Файл mywindow\mywindow.mak
PRJ = mywindow
CC = icc /Ti /c /Ge /Gd- /Se /Re /ss /Gm+
LFLAGS = /NOFREE /NOE /NOD /ALIGN:16 /EXEPACK /M /BASE:0x10000
LINK = ILINK $(LFLAGS)
LIBS = CPPOM30 + OS2386
HEADERS = $(PRJ).h
ALL_OBJ = $(PRJ).obj
.SUFFIXES: .rc .res .obj .lst .c
.c.lst:
$(CC) -Fc$*.lst -Fo$*.obj $*.c
.c.obj:
$(CC) -Fo$*.obj $*.c
.rc.res:
rc -r $*.rc
all: $(PRJ).exe
$(PRJ).l: $(PRJ).mak
echo $(ALL_OBJ) > $(PRJ).l
echo $(PRJ).exe >> $(PRJ).l
echo $(PRJ).map >> $(PRJ).l
echo $(LIBS) >> $(PRJ).l
echo $(PRJ).def >> $(PRJ).l
$(PRJ).res: $(PRJ).rc $(PRJ).ico $(PRJ).h
$(PRJ).obj: $(PRJ).c $(HEADERS)
$(PRJ).exe: $(ALL_OBJ) $(PRJ).def $(PRJ).l $(PRJ).res
$(LINK) @$(PRJ).l
rc -p -x $(PRJ).res $(PRJ).exe
|

