Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых

Программирование для 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
[Назад] [Содеожание] [Дальше]