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


![[Назад]](../../prev.gif)
![[Содеожание]](../../sod.gif)
![[Дальше]](../../next.gif)
