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

Программирование для IBM OS/2

© Александр Фролов, Григорий Фролов
Том 25, М.: Диалог-МИФИ, 1993, 286 стр.

[Назад] [Содеожание] [Дальше]

1.2. Структура приложения Presentation Manager

Теперь, после того как вы познакомились с некоторыми наиболее важными понятиями, мы расскажем о структуре простейшего приложения Presentation Manager и приведем соответствующие фрагменты исходного текста (в дальнейшем для сокращения мы будем называть приложение Presentation Manager просто приложением, а приложение, предназначенное для текстового режима - текстовым приложением).

Инициализация приложения

Прежде чем приложение сможет использовать функции программного интерфейса Presentation Manager, оно должно зарегистрировать себя в системе Presentation Manager при помощи функции WinInitialize . Перед завершением своей работы необходимо вызвать функцию WinTerminate , как это показано ниже:

#define INCL_WIN 
#include <os2.h>
int main ()
{
  // Идентификатор Anchor-block
  HAB  hab;
  hab = WinInitialize (0);
  . . .
  // Строки исходного текста приложения
  . . .
  WinTerminate (hab);
  return(0);
}

Функция WinInitialize имеет единственный параметр, который всегда должен иметь значение, равное нулю.

В случае успешной регистрации функция WinInitialize возвращает идентификатор задачи, который называется Anchor-block handle, а при ошибке - значение NULLHANDLE . Идентификатор Anchor-block имеет тип HAB , который, также как и сама функция, определен в include-файле os2.h.

Как заметил Петцольд в своей книге Programming the OS/2 Presentation Manager, "морской" термин Anchor-block (anchor - якорь) родился в мире больших компьютеров и в контексте операционной системы IBM OS/2 ничего не означает.

Файл os2.h имеет небольшие размеры и содержит команды условного включения других include-файлов, поставляемых в составе систем разработки приложений. Определение INCL_WIN требуется для подключения одного из таких файлов, необходимого для работы с функциями оконого интерфейса Presentation Manager.

Заметьте, что если приложение создает несколько задач, то каждая задача, которая будет вызывать функции программного интерфейса Presentation Manager, должна зарегистрировать себя с помощью функции WinInitialize . В наших первых примерах создается только одна задача, поэтому функция WinInitialize вызывается только один раз в функции main .

В паре с функцией WinInitialize вы должны использовать функцию WinTerminate , назначение которой заключается в освобожении ресурсов, полученных функцией WinInitialize при регистрации задачи в системе Presentation Manager. Забегая вперед, скажем, что перед вызовом функции WinTerminate вы должны уничтожить очередь сообщений, созданную на этапе инициализации приложения, уничтожить все созданные окна и освободить другие ресурсы, полученные у системы Presentation Manager.

При успешном завершении функция WinTerminate возвращает значение TRUE, а при ошибке - FALSE.

Создание очереди сообщений

После регистрации в системе Presentation Manager приложение должно создать очередь сообщений. Для этого необходимо воспользоваться функцией WinCreateMsgQueue . Соответствующий фрагмент кода показан ниже.

#define INCL_WIN
#include <os2.h>
int main ()
{
  HMQ  hmq;
  HAB  hab;
  
  hab = WinInitialize (0);
  hmq = WinCreateMsgQueue (hab, 0);
  if(hmq == NULLHANDLE)
  {
    WinTerminate (hab);
    return(-1);
  }
  . . .
  // Строки исходного текста приложения
  . . .
  WinDestroyMsgQueue (hmq);
  WinTerminate (hab);
  return(0);
}

Прототип функции WinCreateMsgQueue представлен ниже:

HMQ WinCreateMsgQueue (HAB hab, LONG lQueuesize);

Через первый параметр этой функции необходимо передать идентификатор Anchor-block , полученный при регистрации от функции WinInitialize . Второй параметр определяет размер очереди сообщений, причем если указано нулевое значение, используется размер, принятый по умолчанию (10 сообщений).

В случае успеха функция возвращает идентификатор созданной очереди сообщений, который имеет тип HMQ , а при ошибке - значение NULLHANDLE.

Обратите внимание, что если нам не удалось создать очередь сообщений, то перед аварийным завершением работы приложение дожно вызвать функцию WinTerminate .

Для уничтожения очереди сообщений следует использовать функцию WinDestroyMsgQueue , передав ей в качестве параметра идентификатор уничтожаемой очереди. В случае успеха функция возвращает значение TRUE, при ошибке - FALSE.

Если приложение создает несколько задач, оно может также создать для каждой задачи свою очередь сообщений. В этом случае перед завершением работы приложения необходимо уничтожить функцией WinDestroyMsgQueue все созданные очереди сообщений.

Регистрация класса главного окна приложения

Напомним, что окно в системе Presentation Manager можно рассматривать как объект, состоящий из структуры данных и функции окна, реализующей методы для работы с этими данными.

Понятие класса окна используется для установки соответствия между окном и функцией окна. В системе Presentation Manager существуют так называемые предопределенные классы окна, для которых функции окна уже созданы и находятся внутри Presentation Manager.

Если вы создаете окно на базе предопределенного класса окна, оно будет обладать функциональностью, заложенной в него разработчиками Presentation Manager. Для такого окна вам не нужно создавать свою функцию окна, так как эта функция уже создана.

Тем не менее, вы можете переопределить реакцию этой функции на некоторые или все сообщения, создав на базе предопределенного класса собственное окно, поведение которого отличается от стандартного. Таким образом реализуется механизм наследования: вы создаете свое окно на базе предопределенного класса и изменяете только те методы, которые нужно. Наследование сильно упрощает жизнь программистов, так как они получают возможность использовать почти готовые объекты, при необходимости лишь немного изменяя их поведение.

Создавая главное окно приложения, вы должны зарегистрировать собственный класс, определив внутри своего приложения функцию окна. Эта функция будет обрабатывать сообщения, поступающие в очередь сообщений для вашего окна.

Фрагмент кода, в котором выполняется регистрация класса окна для главного окна приложения представлен ниже:

MRESULT EXPENTRY WndProc(HWND, ULONG, MPARAM, MPARAM);
BOOL  fRc;
CHAR  szWndClass[] = "MYWINDOW";

fRc = WinRegisterClass (hab, szWndClass, 
  (PFNWP)WndProc, 0, 0);

if(fRc == FALSE)
{
  WinDestroyMsgQueue (hmq);
  WinTerminate (hab);
  return(-1);
}

Для регистрации класса окна должна использоваться функция WinRegisterClass , прототип которой приведен ниже:

BOOL WinRegisterClass (
 HAB hab,             // идентификатор Anchor-block
 PSZ pszClassName,    // имя класса окна
 PFNWP pfnWndProc,    // функция окна
 ULONG flStyle,       // стиль окна
 ULONG cbWindowData); // дополнительные данные

Через параметр hab этой функции передается идентификатор Anchor-block, полученный от функции WinInitialize при регистрации приложения в системе Presentation Manager.

В параметр pszClassName необходимо записать адрес символьной строки имени регистрируемого класса. Мы указали этот параметр как "MYWINDOW", что выбрано произвольно.

Предопределенные классы окон также имеют свои имена, которые начинаются с префикса WC_, например, WC_BUTTON , WC_COMBOBOX и т. д. Выбирая имя для собственного класса окна, вы должны проследить, чтобы оно случайно не совпало с именем предопределенного класса окна.

С помощью параметра pfnWndProc необходимо указать имя функции, которая будет служить функцией окна для всех окон, создаваемых на базе данного класса.

Заметим, что зарегистрировав класс окна, вы можете создать на его базе несколько окон, и для всех этих окон будет использоваться одна функция окна. Это удобно, если приложение должно создать несколько окон, которые ведут себя одинаково - вам не придется в своем приложении определять несколько одинаковых функций окна.

Приведенный выше фрагмент кода начинается с определения прототипа функции окна, которая в данном случае имеет имя WndProc. Это имя было нами выбрано произвольно.

Параметр flStyle определяет стиль окна, создаваемого на базе класса, регистрируемого функцией WinRegisterClass . После регистрации стиль окна изменить невозможно.

Стиль задается при помощи перечисленных ниже констант, которые можно объединять при помощи логической операции ИЛИ. Если назначение отдельных констант вам пока не вполне понятно, можете пропустить соответстующую строку таблицы. Позже вы можете возвратиться к описанию стилей окна.

Имя константы Описание стиля
CS_CLIPCHILDREN Окно не будет рисовать в области своих дочерних окон. Этот стиль может вызвать замедление процесса перерисовки содержимого окна, однако он может оказаться полезен, если процедура рисования внутри дочерних окон отнимает много времени
CS_CLIPSIBLINGS Стиль используется для дочерних окон, имеющих общее родительское окно и подавляет рисование одного такого окна внутри других дочерних окон, имеющих общего родителя
CS_FRAME Этот стиль имеет главное окно приложения, которое называется Frame Window и используется в качестве платформы для размещения других окон
CS_HITTEST Если окно обладает этим стилем, то если указатель мыши находится в области окна, Presentation Manager будет посылать функции окна сообщения WM_HITTEST , которое будет описано в разделе нашей книги, посвященном мыши
CS_MOVENOTIFY Если установлен этот стиль, то при перемещении окна пользователем по поверхности рабочего стола функция окна будет получать сообщения WM_MOVE
CS_PARENTCLIP Видимая область окна включает область родительского окна
CS_SAVEBITS Этот стиль может быть использован для ускорения перерисовки окна, когда пользователь перемещает его или восстанавливает после минимизации либо скрытия. Ускорение происходит за счет того, что изображение области рабочего стола, занимаемой окном, сохраняется как растровое изображение и затем восстанавливается после изменения расположения окна. Использование этого стиля приводит к неэкономному расходованию оперативной памяти, так как для хранения цветного изображения требуется много места
CS_SIZEREDRAW Если используется этот стиль, то при любом изменении размеров окна его внутренняя область будет полностью перерисована. С этой целью функция окна получит сообщение WM_PAINT
CS_SYNCPAINT Функция окна получит сообщение WM_PAINT сразу после того, как появится необходимость в перерисовке части окна. Если же этот стиль не указан, сообщение WM_PAINT поступит в функцию окна только в том случае, если в очереди нет других сообщений

Теперь рассмотрим последний параметр функции WinRegisterClass , который называется cbWindowData.

С помощью этого параметра вы можете зарезервировать в структуре данных, создаваемой для регистрируемого класса окна, дополнительную память. Объем этой памяти в байтах равен значению параметра cbWindowData. При необходимости вы сможете хранить в этой памяти данные, которые будут доступны всем функциям окон, создаваемых на базе данного класса окна.

Возвращаясь к фрагменту кода, демонстрирующему процесс регистрации класса окна, заметим, что в случае неудачи вы должны уничтожить очередь сообщений функцией WinDestroyMsgQueue и затем вызвать функцию WinTerminate . Если вы этого не сделаете, система может оказаться в неустойчивом состоянии.

Создание главного окна приложения

После создания очереди сообщений и регистрации класса окна все готово для того, чтобы приступить к созданию главного окна приложения.

В общем случае окно создается функцией WinCreateWindow , которую мы рассмотрим немного позже. Для создания главного окна приложения удобнее всего воспользоваться другой функцией, которая называется WinCreateStdWindow .

Для того чтобы вам была понятнее описанная ниже процедура создания главного окна, вспомним, что на самом деле окно приложения, которое видит пользователь, состоит из нескольких окон. Остановимся на этом подробнее.

Главное окно приложения называется Frame Window и на его поверхности расположены все остальные окна, а именно: окно пиктограммы системного меню, заголовок окна, вертикальные и горизонтальные полосы просмотра (если они есть), кнопки минимизации и максимизации окна, окно меню приложения, а также клиентское окно Client Window , внутри которого приложение обычно что-нибудь рисует.

Когда ранее мы говорили про создание главного окна приложения, то несколько упрощали ситуацию. На самом деле необходимо создать окна Frame Window , Client Window , а также другие окна, которые служат в качестве органов управления - системное меню, полосы просмотра и т. д.

К счастью, вы можете создать все эти окна при помощи одного вызова функции WinCreateStdWindow . Задавая соответствующим образом параметры этой функции, вы можете изменять внешний вид окна приложения, создавая (или не создавая) в окне Frame Window те или иные элементы управления.

Особое значение имеет окно Client Window , которое мы будем также называть клиентским окном приложения. Именно в этом окне приложение выполняет рисование, используя его как рабочее (хотя при необходимости приложение может рисовать в любом месте окна Frame Window или даже в любом месте на поверхности рабочего стола графической оболочки Workplace Shell).

Окно Frame Window , как и окна, соответствующие стандартным органам управления (таким как меню или кнопки), создаются на базе классов окна, предопределенных в системе Presentation Manager.

Что же касается окна Client Window , то это окно создается всегда на базе класса, зарегистрированного приложением. Приложение должно определить функцию окна, которая будет обрабатывать сообщения, предназначенные окну Client Window.

Для упрощения клиентское окно Client Window иногда называют главным окном приложения, хотя вы должны понимать, что настоящим главным окном является окно Frame Window . Функция окна Frame Window находится внутри системы Presentation Manager и вам не надо о ней беспокоиться.

Ниже мы привели фрагмент кода, в котором создается главное окно приложения. Здесь предполагается, что создание очереди сообщений и регистрация класса окна уже выполнены.

#define ID_APP_FRAMEWND 1
HWND hWndFrame;
HWND hWndClient;
ULONG flFrameFlags =
    FCF_SYSMENU    | FCF_TITLEBAR       | FCF_MINMAX   |
    FCF_SIZEBORDER | FCF_SHELLPOSITION | FCF_TASKLIST |
    FCF_ICON;

CHAR szAppTitle[] = "My First OS/2 Application";
CHAR szWndClass[] = "MYWINDOW";

hWndFrame = WinCreateStdWindow (HWND_DESKTOP, 
  WS_VISIBLE ,
  &flFrameFlags, szWndClass, szAppTitle,
  0, 0, ID_APP_FRAMEWND, &hWndClient);

if(hWndFrame == NULLHANDLE)
{
  WinDestroyMsgQueue (hmq);
  WinTerminate (hab);
  return(-1);
}

Ниже мы привели прототип функции WinCreateStdWindow с кратким описанием параметров:

HWND WinCreateStdWindow (
  HWND hwndParent, // идентификатор родительского окна
  ULONG flStyle,   // стиль окна Frame Window 
  PULONG pflCreateFlags, // флаги создания 
                         // окна Frame Window 
  PSZ pszClassClient,    // имя класса клиентского окна
  PSZ pszTitle,          // заголовок окна
  ULONG flStyleClient,   // стиль клиентского окна
  HMODULE Resource,      // идентификатор ресурсов
  ULONG ulId,         // идентфикатор окна Frame Window 
  PHWND phwndClient); // идентификатор клиентского окна

В качестве первого параметра hwndParent вы должны передать этой функции идентификатор родительского окна. В следующей главе мы рассмотрим подробнее "семейные" отношения в мире окон Presentation Manager. Сейчас ограничимся таким определением: родительское окно - это то окно, которое создало данное окно и потому связано с ним некоторыми "отношениями". Например, дочернее окно всегда отображается внутри родительского, не выходя за его границы.

В нашем случае в качестве родительского выступает окно рабочего стола, которое имеет идентификатор HWND_DESKTOP , определенный в файле os2.h.

Параметр flStyle определяет стиль окна Frame Window . В качестве стиля вы можете использовать комбинацию перечисленных ниже констант, объединенных при помощи логической операции ИЛИ.

Стиль Описание
WS_SYNCPAINT Синхронное обновление окна. Этот стиль установлен для тех окон, которые созданы на базе класса окна со стилем CS_SYNCPAINT
WS_ANIMATE Включение режима анимации при открывании и закрывании окна. Если этот режим включен, то при изменении размеров окна пользователю будет казаться, что эти размеры плавно изменяются. Заметим, что если анимация окна отключена в блокноте свойств System-Settings, то этот стиль будет проигнорирован
WS_CLIPCHILDREN Если указан этот стиль, то область, занимаемая дочерними окнами, не перерисовывается
WS_CLIPSIBLINGS Если указан этот стиль, то область, занимаемая окнами, имеющими общих родителей, не перерисовывается
WS_DISABLED Окно заблокировано
WS_MAXIMIZED Окно создается максимизированным
WS_MINIMIZED Окно создается минимизированным
WS_PARENTCLIP Если указан этот стиль, окно не рисует вне занимаемой им прямоугольной области
WS_SAVEBITS В момент отображения окна выполняется сохранение графического изображения области под окном, имеющим этот стиль
WS_VISIBLE После создания окно становится видимым. Если этот стиль не указан, то окно создается невидимым, даже если оно не закрыто другими окнами

В нашем примере мы указали стиль WS_VISIBLE , поэтому сразу после создания окна вы увидите его на экране.

Следующий параметр называется pflCreateFlags. Он представляет собой указатель на переменную типа ULONG, в которую перед вызовом функции WinCreateStdWindow необходимо записать флаги создания окна Frame Window .

Ниже мы привели список возможных значений флагов. Эти значения можно объединять при помощи логической операции ИЛИ.

Флаг Описание
FCF_TITLEBAR Если указан этот флаг, в окне Farme Window будет создано окно заголовка
FCF_SYSMENU Будет создано системное меню
FCF_MENU -//- стандартное меню
FCF_MINMAX -//- кнопки минимизации и максимизации
FCF_MINBUTTON -//- кнопка минимизации
FCF_MAXBUTTON -//- кнопка максимизации
FCF_VERTSCROLL -//- вертикальная полоса просмотра
FCF_HORZSCROLL -//- горизонтальная полоса просмотра
FCF_SIZEBORDER -//- рамка для изменения размеров окна
FCF_BORDER Вокруг окна будет нарисована тонкая рамка
FCF_DLGBORDER Вокруг окна будет нарисована рамка, которая используется в стандартных диалоговых панелях
FCF_ACCELTABLE Из ресурса приложения, идентификатор которого указан в параметре Resource функции WinCreateStdWindow , будет загружена таблица акселераторов (будет рассмотрена позже)
FCF_ICON С создаваемым окном связывается пиктограмма, которая идентифицируется параметром Resource функции WinCreateStdWindow . Эта пиктограмма автоматически загружается при создании окна и также автоматически удаляется при уничтожении окна
FCF_SHELLPOSITION Размеры и расположение создаваемого окна определяет оболочка Workplace Shell (а не приложение)
FCF_SYSMODAL Создается системное модальное окно (об этом мы расскажем позже)
FCF_NOBYTEALIGN Если установлен этот флаг, при создании окна не выполняется оптимизация выравнивания соответствующих структур данных в оперативной памяти, которая в некоторых случаях могла бы привести к ускорению процесса отображения окна
FCF_TASKLIST Если установлен этот флаг, к заголовку окна добавляется название программы. Полученная таким образом строка используется при отображении списка задач, запущенных в операционной системе IBM OS/2
FCF_NOMOVEWITHOWNER При перемещении окна, которое является владельцем окна с установленным флагом FCF_NOMOVEWITHOWNER, последнее не должно перемещаться
FCF_AUTOICON Если окно минимизировано, ему не посылается сообщение WM_PAINT , предназначенное для инициирования процедуры перерисовки содержимого окна. Этот флаг оказывает благоприятное влияние на производительность системы
FCF_STANDARD Эквивалент следующей комбинации флагов:

FCF_TITLEBAR | FCF_SYSMENU |

FCF_MINBUTTON | FCF_TASKLIST |

FCF_MAXBUTTON |

FCF_SIZEBORDER | FCF_ICON |

FCF_ACCELTABLE | FCF_MENU |

FCF_SHELLPOSITION

Если указан флаг FCF_STANDARD, следует определить все необходимые ресурсы: пиктограмму приложения, меню, таблицу акселераторов

Обратите внимание, что в качестве параметра pflCreateFlags вы должны передать функции WinCreateStdWindow указатель на слово, содержащее флаги, а не само слово флагов.

Продолжим изучение параметров функции WinCreateStdWindow .

Через параметр pszClassClient вы должны передать функции имя зарегистрированного ранее класса окна Client Window . Напомним, что эта регистрация выполняется с помощью функции WinRegisterClass .

Параметр pszTitle передает функции WinCreateStdWindow указатель на текстовую строку, содержащую заголовок окна. Параметр pszTitle игнорируется, если не указан флаг FCF_TITLEBAR .

Параметр flStyleClient определяет стиль клиентского окна Client Window . Он игнорируется, если параметр pszClassClient содержит указатель на пустую текстовую строку.

Теперь о параметре Resource. Этот параметр указывает идентификатор модуля, содержащий ресурсы приложения. На данном этапе мы будем указывать для этого параметра нулевое значение. При этом загрузка ресурсов будет выполняться из EXE-файла приложения.

Что такое ресурсы приложения ?

Это данные, которые могут быть физически расположены в загрузочном EXE-файле приложения или в DLL-файле библиотеки динамической загрузки. Вы можете таким образом создать и сохранить в загрузочном файле приложения пиктограммы, меню, диалоговые панели, текстовые строки и другие типы данных, в том числе произвольные данные. При запуске приложения те ресурсы, которые не нужны, могут оставаться на диске. Они не будут отнимать у приложений дефицитную оперативную память. В то же время как только они потребуются, операционная система загрузит их в память автоматически.

Более подробное описание приемов работы с ресурсами мы приведем позже. В нашем первом приложении определен только один ресурс - пиктограмма.

Следующий параметр, который вы должны указать для функции WinCreateStdWindow , это параметр ulId. Он задает идентификатор окна Frame Window . Все ресурсы, относящиеся к одному окну Frame Window, должны иметь одинаковый идентификатор. Вы можете использовать любое значение в диапазоне от 0 до 0xFFFF.

Обращаем ваше внимание, что идентификатор окна, полученный от функции WinCreateStdWindow (или WinCreateWindow), и идентификатор, передаваемой этим функциям в качестве параметра ulId - разные по смыслу вещи. Первый из них используется для посылки сообщений окну или выполнения других операций, таких, например, как блокирование. Идентификатор ulId играет важную роль при обработке сообщений, поступающих от данного окна в родительское окно и позволяет определить, от какого окна пришло сообщение. Подробнее об использовании этого идентификатора вы узнаете из главы, посвященной органам управления.

И, наконец, последний параметр phwndClient должен содержать указатель на переменную типа HWND, в которую будет записан идентификатор созданного клиентского окна Client Window . Обращаем внимание, что здесь нужно использовать именно указатель, а не имя переменной.

Запуск цикла обработки сообщений

После вызова функции WinCreateStdWindow окно появится на экране, но оно еще не будет работать. Теперь вы должны запустить цикл обработки сообщений. Основная задача этого цикла - выборка сообщений из очереди приложения и передача их на обработку функции окна, зарегистрированной на предыдущем этапе.

Цикл обработки сообщений выглядит следующим образом:

QMSG   qmsg;
while(WinGetMsg (hab, &qmsg, 0, 0, 0))
  WinDispatchMsg (hab, &qmsg);

Здесь функция WinGetMsg выбирает сообщения из очереди приложения, записывая их в структуру qmsg типа QMSG . Если очередь сообщений пуста, функция переходит в состояние ожидания. Три последних нулевых параметра означают, что из очереди будут выбираться все сообщения для всех окон, созданных в данной задаче.

После того как сообщение выбрано и записано в структуру qmsg, его нужно передать функции окна. Однако вы не можете просто вызвать функцию окна и в качестве одного из параметров передать указатель на данную структуру. Вспомните, мы говорили, что приложение создает функцию окна, но никогда ее не вызывает.

Вместо этого выбранное сообщение передается функции WinDispatchMsg , которая возвращает сообщение системе Presentation Manager и та уже вызывает функцию окна, определенную в вашем приложении.

На первый взгляд такая процедура может показаться бессмысленной, так как система Presentation Manager могла бы и сама выполнять такую обработку внутри себя. Однако цикл обработки сообщений, организованный в приложении, позволяет выполнять дополнительную обработку сообщений перед тем, как они возвратятся системе Presentation Manager и попадут в функцию окна. Такой контроль открывает перед приложением большие возможности в определении поведения своего главного окна. Например, приложение может контролировать весь поток сообщений, проходящих через очередь сообщений этого приложения.

Займемся функцией WinGetMsg , прототип которой приведен ниже:

BOOL WinGetMsg (
 HAB hab,        // идентификатор Anchor-block
 PQMSG  pqmsgmsg, // указатель на структуру,
                 // куда будет записано сообщение
 HWND hwndFilter,// фильтр окон
 ULONG ulFirst,  // начальный код сообщения
 ULONG ulLast);  // конечный код сообщения

Через параметр hab функции WinGetMsg передается идентфикатор Anchor-block, полученный от функции WinInitialize . Здесь для вас нет ничего нового.

Через параметр pqmsgmsg вы должны передать функции указатель на структуру типа QMSG , в которую будет записано извлеченное из очереди сообщение.

Если значение параметра hwndFilter не равно нулю, функция WinGetMsg будет выбирать сообщения, предназначенные для всех окон, созданных в рамках данной задачи. Как правило, именно так и поступают. Однако вы можете указать здесь идентификатор какого-либо дочернего окна и организовать обработку сообщений только для него.

Как мы уже говорили, для идентификации каждому сообщению присваивается определенный код. Если значения параметров ulFirst и ulLast равны нулю, функция WinGetMsg будет обрабатывать сообщения с любым кодом. Если же значение параметра ulFirst больше чем ulLast, будут обрабатываться все сообщения, кроме тех, коды которых попали в интервал от ulLast до ulFirst. Аналогично, если значение параметра ulFirst меньше, чем ulLast, будут обрабатываться только такие сообщения, коды которых попадают в интервал от ulFirst до ulLast.

Как правило, обычно значения параметров ulFirst и ulLast устанавливаются равными нулю.

Завершение работы приложения

Ничто не продолжается бесконечно, поэтому рано или поздно вы должны завершить цикл обработки сообщений и закрыть приложение.

Как это сделать?

Для завершения работы приложения, не имеющего стандартного меню, вы должны сделать двойной щелчок левой клавишей мыши по системному меню, выбрать из этого меню строку Close или нажать комбинацию клавиш <Alt+F4>.

В результате в очередь сообщений записывается сообщение с кодом WM_QUIT , определенным в файле os2.h.

Функция WinGetMsg , выбирающая сообщения из очереди приложения, возвращает значение TRUE для всех сообщений, кроме сообщения с кодом WM_QUIT , чем мы и пользуемся в нашем цикле обработки сообщений. Как только функция WinGetMsg возвращает значение TRUE, цикл обработки сообщений завершается.

Однако пока еще рано вызывать оператор return и возвращать управление операционной системе, завершая работу приложения. Перед этим вам необходимо уничтожить главное окно приложения, очередь сообщений и вызвать функцию WinTerminate .

Финальный фрагмент фнункции main , выполняющий все перечисленные действия, показан ниже:

while(WinGetMsg (hab, &qmsg, 0, 0, 0))
  WinDispatchMsg (hab, &qmsg);
WinDestroyWindow(hWndFrame);
WinDestroyMsgQueue (hmq);
WinTerminate (hab);
return(0);

Для уничтожения главного окна приложения мы вызываем функцию WinDestroyWindow, передавая ей в качестве единственного параметра идентификатор окна Frame Window . При уничтожении окна Frame Window все остальные окна, созданные на его поверхности и являющиеся по отношению к нему дочерними, будут уничтожены автоматически. В частности, будет автоматически уничтожено клиентское окно Client Window , поэтому у вас нет необходимости уничтожать это окно явным образом.

После уничтожения окна Frame Window вы должны уничтожить очередь сообщений, вызав функцию WinDestroyMsgQueue , а затем вызвать функцию WinTerminate . После вызова функции WinTerminate вы не можете пользоваться другими функциями программного интерфейса Presentation Manager. Теперь самое время завершить работу приложения с помощью оператора return, что мы и делаем в приведенном выше примере.

Функция окна

Теперь настало время заняться функцией окна, которое было создано функцией main и после регистрации главного окна приложения, зажило своей жизнью.

Рискуя быть навязчивыми, скажем еще раз, что приложение никогда не должно вызывать функцию окна напрямую. Ей передает управление система Presentation Manager, поэтому она вызывается извне приложения. Такие функции называют функциями обратного вызова.

Прототип функции окна мы привели ниже. Учтите, что имя функции окна может быть любым. Главное, чтобы вы правильно указали это имя при регистрации класса окна функцией WinRegisterClass .

MRESULT EXPENTRY WndProc(
  HWND   hWnd, // идентификатор окна
  ULONG  msg,  // код сообщения
  MPARAM mp1,  // первый параметр сообщения
  MPARAM mp2); // второй параметр сообщения

Через параметр hWnd функции окна передается идентификатор окна. Параметр msg содержит код сообщения, а параметры mp1 и mp2 - первый и второй параметр сообщения, соответственно. Назначение параметров сообщения полностью определяется кодом сообщения.

Сравните параметры функции окна со структурой, в которую записывает сообщение функция WinGetMsg :

typedef struct _QMSG 
{
  HWND   hwnd; // идентификатор окна
  ULONG  msg;  // код сообщения
  MPARAM mp1;  // первый параметр сообщения
  MPARAM mp2;  // второй параметр сообщения
  ULONG  time; // время возникновения сообщения
  POINTL ptl;  // позиция курсора мыши во время
               // возникновения сообщения
  ULONG  reserved;  // зарезервировано
} QMSG ;
typedef QMSG  *PQMSG;

Как видите, функция окна получает сообщение в "разобранном" виде, удобном для анализа.

Анализ выполняется при помощи проверки кода сообщения, например, так, как это показано в следующем фрагменте кода:

MRESULT EXPENTRY
WndProc(HWND hWnd, ULONG msg, MPARAM mp1, MPARAM mp2)
{
  switch (msg)
  {
    case WM_CREATE :
    {
      . . .
    }

    case WM_DESTROY :
    {
      . . .
    }

    case WM_ERASEBACKGROUND :
      return(MRFROMLONG(1L));

    case WM_BUTTON1DOWN :
    {
      . . .
    }

    default:
      return(WinDefWindowProc (hWnd, msg, mp1, mp2));
  }
}

Если функция окна не обрабатывает какое-либо сообщение, она должна передать его функции WinDefWindowProc , выполняющей обработку по умолчанию. Параметры этой функции полностью аналогичны параметрам функции окна.

При создании окна ему передается сообщение WM_CREATE . Обработчик этого сообщения может проверить параметры, переданные функции создания окна, или выполнить какие-либо инициализирующие действия, например, дианмическое получение блока памяти или инициализацию данных. Если такие действия не нужны, функция окна может не обрабатывать это сообщение, передавая его функции WinDefWindowProc .

Аналогично, в процессе уничтожения окна функции окна передается сообщение WM_DESTROY . Обработчик этого сообщения должен освободить ресурсы, заказанные у операционной системы обработчиком сообщения WM_CREATE .

Сравнивая окно с объектом языка программирования С++, можно сказать, что обработчик сообщения WM_CREATE выполняет функцию конструктора, а обработчик сообщения WM_DESTROY - функцию деструктора объекта.

Функция окна может получать сообщения с десятками различных кодов, поступающих от мыши, клавиатуры, таймера, различных органов управления приложения, дочерних окон, операционной системы и других приложений. Мы будем рассказывать вам об этих сообщениях по мере необходимости.

[Назад] [Содеожание] [Дальше]