Программирование для 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 |