- •МIНIСТЕРСТВО ОСВIТИ І НАУКИ УКРАЇНИ
- •КОНСПЕКТ ЛЕКЦІЙ
- •з дисципліни
- •ХАРКIВ 2014
- •Лекция №1: Введение
- •Первая программа
- •Лекция №2: Скелет оконной программы
- •Вступление
- •Лекция №3: Контекст устройства
- •Понятие контекста устройства
- •Объекты и атрибуты контекста устройства
- •Лекция 4: Мышь и таймер
- •Сообщения мыши
- •Захват мыши
- •Таймер
- •Программная реализация
- •Лекция 5: Клавиатура
- •Лекция 6: Дочерние окна
- •Класс кнопок
- •Флажки
- •Переключатели
- •Класс статических дочерних окон
- •Класс полос прокрутки
- •Класс редактирования
- •Класс окна списка
- •Лекция №7: Ресурсы
- •Способы хранения данных программы
- •Общие сведения о файле ресурсов
- •Создание иконок
- •Лекция №8: Ресурсы (меню)
- •Меню
- •MENUITEM SEPARATOR
- •Лекция №9: Ресурсы (быстрые клавиши)
- •Лекция №10: Многозадачность и многопоточность
- •Многопоточность
- •Программная реализация многозадачности
- •Критические разделы
- •Использование событий
- •К вопросу о переменных
- •Инициализация программы
- •Лекция №12: Динамически подключаемые библиотеки
- •Типы связывания
- •Лекция №13: Представление графической информации
- •Метафайлы
- •Лекция № 14: Обзор платформы .NET
- •Обзор платформы .NET
- •Общая схема архитектуры .NET
- •Основные черты MSIL
- •Понятие сборки
- •Манифест: описание сборки
- •Модели безопасности в .NET
- •Електронне навчальне видання
- •КОНСПЕКТ ЛЕКЦІЙ
- •Упорядник ВОЛК Максим Олександрович
#define INIT_MENU_POS 0 #define HELLO_MENU_POS 2 #define RECT_MENU_POS 1
Эти идентификаторы задают положение подменю Window в каждом из трех шаблонов меню. Эта информация необходима программе, чтобы информировать окно -клиент о том , когда должен появиться список документов. Конечно, в меню MdiMenuInit нет подменю Window, поэтому в файле обозначено, что список должен быть присоединен к первому подменю. Однако, фактически здесь список никогда не появится. ( он нужен станет ясно при дальнейшем анализе программы .)
Идентификатор IDM_FIRSTCHILD не соответствует ни одному пункту меню. Этот идентификатор будет связан с первым окном документа в списке, который появляется в подменю Window. Значение этого идентификатора должно быть больше, чем значение всех остальных идентификаторов меню.
Инициализация программы
Вфайле MDIDEMO.С WinMain начинается с регистрации классов окна для главного окна и двух дочерних окон. Их оконные процедуры называются
FrameWndProc, HelloWndProc и RectWndProc. Как правило, с каждым из этих трех классов связан свой значок. Для простоты в программе и для главного и для дочерних окон использован стандартный значок IDI_APPLICATION. Обратите внимание, что в поле hbrBackground структуры WNDCLASSEX для класса главного окна задан системный цвет COLOR_APPWORKSPACE. Однако, когда главное окно появляется на экране первым, одинаковый цвет смотрится несколько лучше.
Вполе lpszMenuName заносится значение NULL для каждого из трех классов окна. Для классов окна дочерних окон Hello и Rect это нормально. Для главного окна это сделано для того, чтобы указать описатель меню при создании главного окна в функции CreateWindow.
Далее WinMain использует функцию LoadMenu для загрузки трех меню и сохраняет их описатели в глобальных переменных. Три вызова функции GetSubMenu позволяют получить описатели подменю Window, в которому будет добавлен список окон документов. Они также запоминаются в глобальных переменных. Функция LoadAccelerators загружает таблицу быстрых клавиш.
Вызов функции CreateWindow в WinMain создает главное окно. При обработке в FrameWndProc сообщения WM_CREATE главное окно создает окно – администратор. При этом еще раз вызывается функция CreateWindow. Класс окна задается как MDICLIENT, который представляет собой уже зарегистрированный в системе класс для окна – MDI. Последний параметр функции CreateWindow должен быть указателем на структуру типа CLIENTCREATESTRUCT. В этой структуре имеется два поля:
92
hWindowMenu является описателем подменю, в котором появится список документов. В программе MDIDEMO это описатель hMenuInitWindow, полученный в WinMain. Позже мы узнаем, как изменить меню.
idFirstChild является идентификатором меню , относящимся к первому окну документа в списке документов.
Он просто равен IDM_FIRSTCHILD.
Вернемся к WinMain. Программа MDIDEMO выводит на экран только что созданное главное окно и входит в цикл обработки сообщений. Этот цикл обработки сообщений немного отличается от обычного: после получения сообщения из очереди при помощи функции GetMessage программа MDI передает сообщение функции TranslateMDISysAccel (функции TranslateAccelerator, если, как и в программе MDIDEMO, в программе также имеются быстрые клавиши меню ).
Функция TranslateMDISysAccel преобразует любые комбинации клавиш, которые могут соответствовать специальным быстрым клавишам MDI (<Ctrl>+<F6>), в сообщение WM_SYSCOMMAND. Если одна из функций
TranslateMDISysAccel или TranslateAccelerator возвращает TRUE (означает, что сообщение было преобразовано одной из этих функций ), то вызова функции
TranslateMessage и функции DispatchMessage не происходит.
Обратите внимание на два описателя окон, передаваемые функциям
TranslateMDISysAccel и TranslateAccelerator: соответственно hwndClient и hwndFrame. Функция WinMain получает описатель окна hwndClient, используя вызов функции GetWindow с параметром GW_CHILD.
Создание дочерних окон
Часть FrameWndProc связана с обработкой сообщений WM_COMMAND, которые информируют о выборе какого – либо пункта меню. Как обычно, параметр wParam сообщения в FrameWndProc содержит идентификатор меню.
При значениях параметра wParam IDM_NEWHELLO и IDM_NEWRECT,
FrameWndProc должна создать новое окно документа. Это требует инициализации полей структуры MDICREATESTRUCT (часть которых соответствует параметрам функции CreateWindow) и отправки окну – сообщения WM_MDICREATE с параметром lParam, равным указателю на эту структуру. Затем окно – создает дочернее окно документа. FrameWndProc, вызывая функцию CreateMDIWindow, могла бы сама создать это дочернее окно. Для программы, имеющей один поток, такой как MDIDEMO, можно выбрать любой из этих методов.
Как правило, поле szTitle структуры MDICREATESTRUCT является именем файла, соответствующего документу. В поле style могут быть заданы стили окна WS_HSCROLL, или WS_VSCROLL, или оба вместе, что позволяет включить в окно документа полосы прокрутки . ( ShowScrollBar для вывода полос прокрутки на экран вызывать не обязательно). В поле style могут быть также указаны стили WS_MINIMIZE или WS_MAXIMIZE для первого появления окна документа в свернутом или развернутом состоянии.
93
Поле lParam структуры MDICREATESTRUCT дает возможность главному и дочернему окну использовать некоторые общие переменные. Этому полю можно присвоить значение описателя памяти , соответствующего блоку памяти , содержащему структуру . При обработке сообщения WM_CREATE в дочернем окне документа , параметр lParam – это указатель на структуру CREATESTRUCT, а поле lpCreateParams этой структуры – это указатель на структуру MDICREATESTRUCT, используемую для создания окна .
При получении сообщения WS_MDICREATE окно – создает дочернее окно документа и добавляет заголовок окна к нижней части подменю, заданного в структуре MDICREATESTRUCT, которая используется для создания окна. Когда программа MDIDEMO создает свое первое окно документа, этим подменю является подменю File меню MdiMenuInit. Позже мы увидим, как этот список документов переносится в подменю Window меню
MdiMenuHello и MdiMenuRect.
В меню может быть перечислено до девяти документов, перед каждым из которых ставится номер от 1 до 9. Номер подчеркивается. Если создается более девяти окон документов, то за этим списком появляется пункт меню "More windows". Выбор этого пункта вызывает появление окна диалога содержащего список, в котором перечислены все окна документов. Поддержка такого списка документов – это одно из самых лучших характеристик поддержки MDI в
Windows.
Дополнительная информация об обработке сообщений в главном
окне
Перед тем, как перейти к рассмотрению дочерних окон документов, разберемся с обработкой сообщений в FrameWndProc. При выборе в меню File опции Close программа MDIDEMO закрывает активное дочернее окно. Описатель активного дочернего окна она получает, посылая окну – сообщение WM_MDIGETACTIVE. Если дочернее окно отвечает утвердительно на сообщение WM_QUERYENDSESSION, то программа MDIDEMO для закрытия дочернего окна посылает окну – сообщение WM_MDIDESTROY.
Для обработки опции Exit меню File необходимо только, чтобы оконная процедура главного окна послала себе сообщение WM_CLOSE.
Обработать опции Tile, Cascade и Arrange Icons из подменю Window
проще простого, нужно только послать окну – сообщения WM_MDITILE, WM_MDICASCADE и WM_MDIICONARRANGE.
Обработка опции Close All несколько сложнее. FrameWndProc вызывает функцию EnumChildWindows, передавая указатель на функцию CloseEnumProc. Эта функция посылает сообщение WM_MDIRESTORE каждому дочернему окну, затем сообщение WM_QUERYENDSESSION и сообщение WM_MDIDESTROY. Этого не делается для окна заголовка значка, определяемого, если возвращаемое значение функции GetWindow c параметром
GW_OWNER не равно NULL.
94
Все сообщения , которые оконная процедура главного окна не обрабатывает , должны передаваться в DefFrameProc. Эта функция заменяет в оконной процедуре главного окна функцию DefWindowProc. Даже если оконная процедура главного окна и перехватывает сообщения WM_MENUCHAR, WM_SETFOCUS или WM_SIZE, все равно они должны передаваться в
DefFrameProc.
Необрабатываемые сообщения WM_COMMAND также должны передаваться в DefFrameProc. В частности, FrameWndProc не обрабатывает сообщений WM_COMMAND, появившихся в результате того, что пользователь выбирает один из документов из списка в подменю Window. ( параметра wParam для этих опций начинается с IDM_FIRSTCHILD.) Эти сообщения передаются в DefFrameProc и обрабатываются там. Обратите внимание , что главному окну не нужно поддерживать список описателей окон всех созданных документов . При необходимости (, при обработке опции Close All из меню ), эти описатели можно получить , вызывая функцию EnumChildWindows.
Дочерние окна документов
Теперь рассмотрим HelloWndProc – оконную процедуру тех дочерних окон документов , которые выводят на экран фразу "Привет, мир!".
Как и для любого класса окна, который используется более , чем для одного окна , статические переменные, определенные в оконной процедуре (и в любой функции , вызываемой из оконной процедуры ), совместно используются всеми окнами , созданными на основе этого класса окна. Данные, уникальные для каждого окна , должны храниться в форме , отличной от статических переменных. Один из таких приемов подразумевает применение свойств окна. При другом подходе используется область памяти , зарезервированная путем определения отличного от нуля значения в поле cbWndExtra структуры WNDCLASSEX, используемой при регистрации класса окна.
Оконная процедура окна документа получает сообщение WM_MDIACTIVATE всегда, когда окно становится активным или перестает быть активным (в зависимости от того, содержится ли описатель окна в параметре lParam этого сообщения ). Вспомните, что в программе MDIDEMO имеется три различных меню: MdiMenuInit выводится, если нет ни одного окна документа, MdiMenuHello выводится, если активно окно документа Hello, и MdiMenuRect выводится , если активно окно документа с прямоугольниками .
Сообщение WM_MDIACTIVATE дает возможность окну документа изменить меню. Если в параметре lParam этого сообщения содержится описатель окна (окно становится активным), HelloWndProc изменяет меню на MdiMenuHello. Если в параметре lParam этого сообщения содержится описатель другого окна, HelloWndProc преобразует меню в MdiMenuInit.
HelloWndProc изменяет меню путем отправки сообщения WM_MDISETMENU окну – администратору. Окно – администратору обрабатывает это сообщение, удаляя список документов из текущего меню и
95
присоединяя его к новому меню. Таким образом список документов попадает из меню MdiMenuInit (является результатом создания первого документа) в меню MdiMenuHello. Не используйте для изменения меню в приложении MDI функцию SetMenu.
Значения параметров wParam и lParam сообщения WM_MDIACTIVATE являются, соответственно, описателями окна , которое перестает быть активным, и окна, становящегося активным. Оконная процедура получает первое сообщение WM_MDIACTIVATE с параметром lParam равным описателю текущего окна , когда это окно впервые создается , а когда окно закрывается , она получает последнее сообщение WM_MDIACTIVATE с параметром lParam равным другому значению . Когда пользователь переключается с одного документа на другой, первое окно документа получает сообщение WM_MDIACTIVATE с параметром lParam равным описателю первого окна ( это время оконная процедура устанавливает меню в MdiMenuInit). Второе окно документа получает сообщение WM_MDIACTIVATE с параметром wParam равным описателю второго окна (это время оконная процедура устанавливает меню либо в MdiMenuHello, либо в MdiMenuRect, в зависимости от описателя). При закрытии всех окон документов остается только меню MdiMenuInit. Вспомните, что FrameWndProc посылает дочернему окну асинхронное сообщение WM_QUERYENDSESSION, когда пользователь выбирает в меню опцию Close или Close All. HelloWndProc обрабатывает сообщения WM_QUERYENDSESSION и WM_CLOSE, выводя на экран окно сообщений с запросом пользователю о том, можно ли закрывать окно . ( реальной программе в этом окне сообщений может появляться запрос о том , нужно ли сохранять файл .) Если пользователь выбирает опцию , соответствующую тому , что окно закрывать не следует , оконная процедура возвращает 0.
Все необрабатываемые сообщения должны передаваться в DefMDIChildProc (не в DefWindowProc) для их обработки по умолчанию. Некоторые сообщения должны быть переданы в DefMDIChildProc независимо от того, обрабатываются ли они как – в оконной процедуре или нет . Такими сообщениями являются:
WM_CHILDACTIVATE, WM_GETMINMAXINFO, WM_MOVE,
WM_SETFOCUS, WM_SIZE, WM_MENUCHAR и WM_SYSCOMMAND.
RectWndProc почти полностью аналогична HelloWndProc, поэтому нет смысла ее специально рассматривать.
Освобождение захваченных ресурсов
Программа MDIDEMO в функции WinMain использует функцию LoadMenu для загрузки трех меню, определенных в файле описания ресурсов. Обычно Windows удаляет меню, когда закрывается окно, к которому меню относится. Это касается и меню MdiMenuInit. Однако, меню, не относящиеся к какому бы то ни было окну (программе MDIDEMO это меню Hello и Rect), будут продолжать занимать некоторую область памяти, даже после завершения
96
