Программирование для IBM OS/2© Александр Фролов, Григорий ФроловТом 4, М.: Диалог-МИФИ, 1993, 286 стр. 2.5. Приложение WINTREEНаше следующее приложение называется WINTREE. Оно создает два окна верхнего уровня и одно дочернее окно (рис. 2.1), демонстрируя древовидную структуру родительских отношений между окнами.
Рис. 2.1. Окна, создаваемые приложением WINTREE Изучая это приложение, вы научитесь создавать дочерние окна, а также сможете изучить поведение таких окон, сравнив их с поведением окон верхнего уровня. Исходный текст приложения WINTREE показан в листинге 2.1. Листинг 2.1. Файл wintree\wintree.c // =================================================== // Определения // =================================================== #define INCL_WIN #define INCL_GPI #define INCL_WINDIALOGS #include <os2.h> #include <stdio.h> #include "wintree.h" // Прототип функции окна приложения MRESULT EXPENTRY WndProc1(HWND, ULONG, MPARAM, MPARAM); MRESULT EXPENTRY WndProc2(HWND, ULONG, MPARAM, MPARAM); MRESULT EXPENTRY WndProcChild(HWND, ULONG, MPARAM, MPARAM); // ================================================== // Глобальные переменные // ================================================== // Идентификатор Anchor-block HAB hab; // Идентификатор первого и второго окна Frame Window HWND hWndFrame1; HWND hWndFrame2; // Идентификатор дочернего окна HWND hWndChildFrame; // Идентификатор первого и второго окна Client Window HWND hWndClient1; HWND hWndClient2; // Идентификатор окна Client Window дочернего окна HWND hWndChildClient; // Заголовки окон CHAR szAppTitle1[] = "Windows Tree Demo 1"; CHAR szAppTitle2[] = "Windows Tree Demo 2"; CHAR szChildTitle[] = "Child Window"; // =================================================== // Главная функция приложения 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; // Флаги для создания дочернего окна ULONG flFrameChildFlags = FCF_SYSMENU | FCF_TITLEBAR | FCF_MINMAX | FCF_SIZEBORDER; // Имена классов для создаваемых окон CHAR szWndClass1[] = "WINTREE1"; CHAR szWndClass2[] = "WINTREE2"; CHAR szWndClassChild[] = "WINTREECHILD"; // Инициализация приложения 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 (hab); return(-1); } // Регистрация класса главного окна приложения fRc = WinRegisterClass (hab, szWndClass1, (PFNWP)WndProc1, 0, 0); if(fRc == FALSE) { WinMessageBox (HWND_DESKTOP, HWND_DESKTOP, "Ошибка при регистрации класса главного окна", "Ошибка", 0, MB_ICONHAND | MB_OK); WinDestroyMsgQueue (hmq); WinTerminate (hab); return(-1); } // Регистрация класса второго окна fRc = WinRegisterClass (hab, szWndClass2, (PFNWP)WndProc2, 0, 0); if(fRc == FALSE) { WinMessageBox (HWND_DESKTOP, HWND_DESKTOP, "Ошибка при регистрации класса главного окна", "Ошибка", 0, MB_ICONHAND | MB_OK); WinDestroyMsgQueue (hmq); WinTerminate (hab); return(-1); } // Регистрация класса дочернего окна fRc = WinRegisterClass (hab, szWndClassChild, (PFNWP)WndProcChild, 0, 0); if(fRc == FALSE) { WinMessageBox (HWND_DESKTOP, HWND_DESKTOP, "Ошибка при регистрации класса главного окна", "Ошибка", 0, MB_ICONHAND | MB_OK); WinDestroyMsgQueue (hmq); WinTerminate (hab); return(-1); } // Создаем главное окно приложения hWndFrame1 = WinCreateStdWindow (HWND_DESKTOP, WS_VISIBLE , &flFrameFlags, szWndClass1, szAppTitle1, 0, 0, ID_APP_FRAMEWND, &hWndClient1); if(hWndFrame1 == NULLHANDLE) { WinMessageBox (HWND_DESKTOP, HWND_DESKTOP, "Ошибка при создании главного окна", "Ошибка", 0, MB_ICONHAND | MB_OK); WinDestroyMsgQueue (hmq); WinTerminate (hab); return(-1); } // Создаем второе окно hWndFrame2 = WinCreateStdWindow (HWND_DESKTOP, WS_VISIBLE , &flFrameFlags, szWndClass2, szAppTitle2, 0, 0, ID_APP_FRAMEWND, &hWndClient1); if(hWndFrame2 == NULLHANDLE) { WinMessageBox (HWND_DESKTOP, HWND_DESKTOP, "Ошибка при создании второго окна", "Ошибка", 0, MB_ICONHAND | MB_OK); WinDestroyWindow(hWndFrame1); WinDestroyMsgQueue (hmq); WinTerminate (hab); return(-1); } // Создаем дочернее окно hWndChildFrame = WinCreateStdWindow (hWndFrame2, WS_VISIBLE , &flFrameChildFlags, szWndClassChild, szChildTitle, 0, 0, ID_CHILDWND, &hWndChildClient); if(hWndChildFrame == NULLHANDLE) { WinMessageBox (HWND_DESKTOP, HWND_DESKTOP, "Ошибка при создании дочернего окна", "Ошибка", 0, MB_ICONHAND | MB_OK); WinDestroyWindow(hWndFrame2); WinDestroyWindow(hWndFrame1); WinDestroyMsgQueue (hmq); WinTerminate (hab); return(-1); } // Устанавливаем начальные размеры и // расположение дочернего окна WinSetWindowPos (hWndChildFrame, HWND_TOP , 10, 10, 200, 200, SWP _ACTIVATE | SWP_SIZE | SWP_SHOW | SWP_MOVE ); // Устанавливаем пиктограмму для дочернего окна WinSendMsg (hWndChildFrame, WM_SETICON , (MPARAM)WinQuerySysPointer (HWND_DESKTOP, SPTR_APPICON, FALSE), NULL); // Запускаем цикл обработки сообщений while(WinGetMsg (hab, &qmsg, 0, 0, 0)) WinDispatchMsg (hab, &qmsg); // Уничтожаем главное окно приложения WinDestroyWindow(hWndFrame1); // Удаляем очередь сообщений и вызываем // функцию WinTerminate WinDestroyMsgQueue (hmq); WinTerminate (hab); // Возвращаем управление операционной системе return(0); } // ================================================== // Функция главного окна приложения // ================================================== MRESULT EXPENTRY WndProc1(HWND hWnd, ULONG msg, MPARAM mp1, MPARAM mp2) { CHAR szMsg[100]; switch (msg) { case WM_ERASEBACKGROUND : return(MRFROMLONG(1L)); case WM_BUTTON1DOWN : { sprintf (szMsg, "(x, y) = (%ld, %ld)", SHORT1FROMMP (mp1), SHORT2FROMMP (mp1)); WinMessageBox (HWND_DESKTOP, hWnd, szMsg, "Координаты курсора мыши (окно 1)", 0, MB_INFORMATION | MB_OK); } default: return(WinDefWindowProc (hWnd, msg, mp1, mp2)); } } // ================================================== // Функция второго окна приложения // ================================================== MRESULT EXPENTRY WndProc2(HWND hWnd, ULONG msg, MPARAM mp1, MPARAM mp2) { CHAR szMsg[100]; switch (msg) { case WM_ERASEBACKGROUND : return(MRFROMLONG(1L)); case WM_BUTTON1DOWN : { sprintf (szMsg, "(x, y) = (%ld, %ld)", SHORT1FROMMP (mp1), SHORT2FROMMP (mp1)); WinMessageBox (HWND_DESKTOP, hWnd, szMsg, "Координаты курсора мыши (окно 2)", 0, MB_INFORMATION | MB_OK); } default: return(WinDefWindowProc (hWnd, msg, mp1, mp2)); } } // ================================================== // Функция дочернего окна // ================================================== MRESULT EXPENTRY WndProcChild(HWND hWnd, ULONG msg, MPARAM mp1, MPARAM mp2) { CHAR szMsg[100]; switch (msg) { case WM_ERASEBACKGROUND : return(MRFROMLONG(1L)); case WM_BUTTON1DOWN : { sprintf (szMsg, "(x, y) = (%ld, %ld)", SHORT1FROMMP (mp1), SHORT2FROMMP (mp1)); WinMessageBox (HWND_DESKTOP, hWnd, szMsg, "Координаты курсора мыши (дочернее окно)", 0, MB_INFORMATION | MB_OK); } default: return(WinDefWindowProc (hWnd, msg, mp1, mp2)); } } Файл wintree.hФайл wintree.h, приведенный в листинге 2.2, содержит определение констант ID_APP_FRAMEWND и ID_CHILDWND (идентификаторы ресурсов для окон верхнего уровня и дочернего окна). Листинг 2.2. Файл wintree\wintree.h #define ID_APP_FRAMEWND 1 #define ID_CHILDWND 2 Файл wintree.rcФайл описания ресурсов приложения (листиг 2.3) содержит определение пиктограммы. Листинг 2.3. Файл wintree\wintree.rc #include <os2.h> #include "wintree.h" ICON ID_APP_FRAMEWND WINTREE.ICO Файл wintree.defВ файле определения модуля (листинг 2.4) в секции EXPORTS перечислены имена всех функций обратного вызова. В роли этих функций выступают функции окон верхнего уровня WndProc1 и WndProc2, а также функция дочернего окна WndProcChild. Листинг 2.4. Файл wintree\wintree.def NAME WINTREE WINDOWAPI DESCRIPTION 'WinTree Application (C) Frolov A., 1996' HEAPSIZE 4096 STACKSIZE 32768 EXPORTS WndProc1 WndProc2 WndProcChild ОпределенияВ самом начале исходного текста приложения определены три прототипа функций окна. Две из этих функций соответствуют окнам верхнего уровня, одна - дочернему окну. Для каждого из перечисленных выше трех окон в области глобальных переменных хранятся идентификаторы окна Frame Window (hWndFrame1, hWndFrame2 и hWndChildFrame), а также идентификаторы окон Client Window (hWndClient1, hWndClient2 и hWndChildClient). Кроме этого, в области глобальных переменных находятся заголовки для создаваемых окон (szAppTitle1, szAppTitle2 и szChildTitle). Функция mainВ функции main определены флаги, которые используются для создания окон верхнего уровня (переменная flFrameFlags) и для окна дочернего уровня (переменная flFrameChildFlags). Обратите внимание, что для дочернего окна мы не используем флаги FCF_ICON , FCF_SHELLPOSITION и FCF_TASKLIST . Для установки пиктограммы, отображемой в верхнем левом углу дочернего окна, мы посылаем этому окну сообщение WM_SETICON . Размеры и расположение дочернего окна будут установлены явням образом при помощи функции WinSetWindowPos . Каждое из трех окон нашего приложения имеет свою функцию окна и потому создается на базе своего класса окна. Имена этих классов хранятся в переменных szWndClass1, szWndClass2 и szWndClassChild. Свою работу функция main начинает, как обычно, с вызова функции инициализации WinInitialize . Далее при помощи функции WinCreateMsgQueue создается очередь сообщений. После этого для регистрации классов окна три раза вызывается функция WinRegisterClass . При этом определяется, что для окон, создаваемых на базе класса szWndClass1, будет использоваться функция окна WndProc1, для окон, создаваемых на базе класса szWndClass2 - функция окна WndProc2, а для окон, создаваемых на базе класса szWndClassChild - функция окна WndProcChild. Как и раньше, после вызова каждой функции проверяется код возврата. Если при выполнении функции произошла ошибка, приложение выводит на экран соответствующее сообщение и завершает свою работу. Если регистрация классов завершилась успешно, функция main создает два окна верхнего уровня, аналогично тому, как создавалось главное окно предыдущего приложения. Каждое из этих окон основано на базе своего класса окна и имеет собственную функцию окна, однако для обоих окон мы указали один и тот же идентификатор ресурсов. В результате эти окна будут иметь одинаковую пиктограмму системного меню. На следующем этапе наше приложение создает окно, которое является дочерним по отношению к второму окну верхнего уровня, имеющему идентификатор hWndFrame2. Для этого используется знакомая вам функция WinCreateStdWindow : hWndChildFrame = WinCreateStdWindow (hWndFrame2, WS_VISIBLE , &flFrameChildFlags, szWndClassChild, szChildTitle, 0, 0, ID_CHILDWND, &hWndChildClient); В качестве идентификатора ресурса для дочернего окна указана константа ID_CHILDWND. В файле описания ресурсов нет пиктограммы с таким идентификатором, поэтому если не предпринимать никаких дополнительных мер, для системного меню будет использована стандартная пиктограмма, определенная в Presentation Manager. После создания дочернего окна функция main устанавливает его размеры и расположение, вызывая для этого рассмотренную нами ранее функцию WinSetWindowPos : WinSetWindowPos (hWndChildFrame, HWND_TOP , 10, 10, 200, 200, SWP _ACTIVATE | SWP_SIZE | SWP_SHOW | SWP_MOVE ); Далее для изменения пиктограммы системного меню при помощи функции WinSendMsg в функцию дочернего окна посылается сообщение WM_SETICON : WinSendMsg (hWndChildFrame, WM_SETICON , (MPARAM)WinQuerySysPointer (HWND_DESKTOP, SPTR_APPICON, FALSE), NULL); Первый параметр этого сообщения должен содержать идентификатор пиктограммы, второй не используется и указан как NULL. Функция WinQuerySysPointer предназначена для получения идентификатора ресурса системного курсора или системной пиктограммы, заданной своим идентификатором. Прототип этой функции приведен ниже: HPOINTER WinQuerySysPointer ( HWND hwndDeskTop, // идентификатор окна Desktop Window LONG lIdentifier, // идентификатор системного курсора // или пиктограммы BOOL fCopy); // признак копирования В качестве значения для параметра hwndDeskTop можно
использовать константу HWND_DESKTOP. Что же касается
идентификатора lIdentifier, то для него следует
указывать одно из приведенных ниже значений.
Признак копирования fCopy может принимать значения TRUE или FALSE. В первом случае создается копия указанного системного ресурса, которая при необходимости может быть модифицирована. Ресурс, созданный таким образом, должен быть удален после использования при помощи функции WinDestroyPointer . Эта функция имеет единественный параметр - идентификатор уничтожаемого ресурса. Наше приложение ничего не модифицирует и использует значение FALSE. При этом функция WinQuerySysPointer просто возвращает идентификатор, который мы передаем дочернему окну через сообщение WM_SETICON . Функции оконФункции окон, определенные в нашем приложении, похожи друг на друга и на функцию окна из предыдущего приложения. Их основная работа заключается в том, чтобы выводить на экран сообщение, когда пользователь делает щелчок левой клавишей мыши в области окна. В сообщении указывается название окна и координаты курсора мыши (в системе координат, связанной с этим окном). |