Программная реализация многозадачности 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Программная реализация многозадачности



 

 

Когда пользователь запускает программу, Windows создает в памяти компьютера экземпляр программы, называемый процессом. Процесс не является точной копией *.ехе – файла, как это было, например в операционной системе ДОС. Процесс содержит в себе копию *.ехе – файла, а также некоторую другую информацию о функционировании данного приложения. В этой дополнительной информации хранятся, например, границы выделенной приложению памяти, что помогает аппаратно отслеживать корректности обращения к оперативной памяти со стороны приложения. Так как Windows поддерживает механизм виртуальной памяти, то среди этой информации находятся сведения о расположении сегментов программы. Здесь же содержится командная строка, формируемая при запуске программы.

 

 

Запустить процесс можно как при помощи командной строки системного меню Windows, при помощи программы Проводника, так и программно, из другого приложения. Это можно выполнить при помощи функций API:

 

 

Для 16-битных приложений используется функция WinExec:

 

 

UINT WinExec(

 

 

LPCSTR lpCmdLine, // command line

 

 

UINT uCmdShow // window style

 

 

);

 

 

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

 

 

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

 

 

В среде Windows 32 следует использовать другой способ порождения процессов:

 

 

BOOL CreateProcess(

 

 

LPCTSTR lpApplicationName, // pointer to name of executable module

 

 

LPTSTR lpCommandLine, // pointer to command line string

 

 

LPSECURITY_ATTRIBUTES lpProcessAttributes, // pointer to process security attributes

 

 

LPSECURITY_ATTRIBUTES lpThreadAttributes, // pointer to thread security attributes


– 75 –

 

 

BOOL bInheritHandles, // handle inheritance flag

 

 

DWORD dwCreationFlags, // creation flags

 

 

LPVOID lpEnvironment, // pointer to new environment block

 

 

LPCTSTR lpCurrentDirectory, // pointer to current directory name

 

 

LPSTARTUPINFO lpStartupInfo, // pointer to STARTUPINFO

 

 

LPPROCESS_INFORMATION lpProcessInformation // pointer to PROCESS_INFORMATION

 

 

);

 

 

Первый параметр является указателем на имя запускаемого файла. Имя может содержать полный путь к файлу (диск:\каталог\…\файл). Если имя не содержит пути, то операционная система ищет файл в текущем каталоге, затем в системных каталогах и в каталогах, указанных в разделе PATH при загрузке системы.

 

 

Второй параметр указывает на командную строку.

 

 

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

 

 

bInheritHandles и dwCreationFlags содержит дополнительные флаги управления созданием и приоритетом процесса.

 

 

lpEnvironment содержит указатель на буфер памяти, в котором будет создаваться служебная информация по процессу. Если равен NULL, то операционная система сама отводит место в памяти под эту информацию.

 

 

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

 

 

lpStartupInfo определяет структуру STARTUPINFO, которая описывает окно, создаваемое для запускаемого процесса (в ней содержится информация, похожая на ту, которая передается в процедуру CreateWindow).

 

 

lpProcessInformation указатель на структуру, заполняемую после создания нового процесса. Структура содержит информацию о созданном процессе.

 

 

Многопоточность в программе реализовать можно несколькими путями.

 

 

·Непосредственное использование системного таймера для указание процедуры, вызываемой периодически. Данный способ был рассмотрен в лекции "Использование Таймера". Это типичный представитель вытесняющей многозадачности. К достоинствам этого способа можно отнести то, что программа может устанавливать и изменять период вызова функции.

 

 

·Использование системного таймера для организации посылки синхронных сообщений выбранному окну (порядок организации описан в лекции "Использование Таймера"). С помощью данного способа реализуется невытесняющая многозадачность. Достоинство – изменяемая периодичность посылки сообщений. Недостаток – природа синхронных сообщений не гарантирует четкое выполнение периода прихода сообщений от таймера.


 

 

·Создание потоков. Данный способ подразумевает определение некоторой процедуры потока, которая запускается параллельно основному процессу приложения. Момент окончания выполнения потока контролирует сама поточная процедура.

 

 

CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)Thread1,&params,0,&iThread);

 

 

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

 

 

Второй параметр определяет начальный адрес потока (фактически - имя процедуры потока), например:

 

 

DWORD WINAPI Thread1(PVOID pvoid)

 

 

{

 

 

// Текст потока

 

 

}

 

 

Третий параметр является целым числом, которое передается создаваемому потоку как параметр (PVOID pvoid). В данном случае в качестве данного параметра выступает указатель на некоторую структуру данных. Таким образом, основной процесс имеет возможность передавать в поток необходимую информацию.

 

 

Четвертый параметр определяет дополнительные флаги создания потока. Если этот параметр равен нулю, то поток создастся немедленно.

 

 

Последний параметр является адресом переменной, в которую возвращается идентификатор потока.

 

 

Использование функции Sleep

 

 

Итак, поток – это часть программы, запускаемая параллельно другим задачам процесса. Поток сам определяет как долго ему находится в памяти и какие действия надо совершать. Если поток должен периодически совершать одни и те же действия (например, обновлять экран или проверять почту), то самым естественным способом сделать это является организация бесконечного цикла (подобного циклу обработки сообщений). Однако, в этом случае не определено время цикличности, т.к. временные периоды выполнения потока определены операционной системой. Кроме того, как только одна итерация цикла заканчивается, начинается другая. Как же вызвать приостановку выполнения потока? Для этих целей существует функция Sleep, которая в качестве единственного параметра имеет время, задаваемое в миллисекундах. Функция не осуществляет возврата до тех пор, пока не истечет указанное время. При этом другие потоки и процессы работают в обычных режимах. Если параметр этой функции равен нулю, то операционная система просто лишает текущий поток оставшегося кванта времени.

 

 

Следует отметить, что данная функция не освобождает полностью процессор от исполнения потока. Действительно, процессор периодически должен проверять истекло ли время, заданное в функции. То есть, речь идет не о приостановке периодического выполнения потока, а лишь о задержке выполнения алгоритма потока на одной его команде (Sleep).

 

 

Критические разделы

 

 

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


– 77 –

 

 

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

 

 

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

 

 

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

 

 

Данный участок потока называется критическим разделом. Существует четыре функции работы с критическим разделом. Чтобы их использовать необходимо определеить объект типа критический раздел:

 

 

CRITICAL_SECTION cs;

 

 

Объект типа критический раздел сначала должен быть инициализирован одним из потоков программы с помощью функции:

 

 

InitializeCriticalSection(&cs);

 

 

Эта функция создает критический раздел с именем cs. После инициализации объекта "критический раздел" поток входит в критический раздел, вызывая функцию:

 

 

EnterCriticalSection(&cs);

 

 

В этот момент поток становиться владельцем объекта. Два различных потока не могут быть владельцами одного объекта одновременно. Следовательно, если один поток вошел в критический раздел, то следующий поток, вызывая функцию EnterCriticalSection(&cs); с тем же самым объектом, будет задержан внутри функции. Возврат из функции произойдет только тогда, когда первый поток покинет критический раздел, вызвав функцию:

 

 

LeaveCriticalSection(&cs);

 

 

В этот момент второй поток, задержанный в функции EnterCriticalSection(&cs), станет владельцем критического раздела, и его выполнение будет возобновлено.

 

 

Когда критический раздел больше не нужен вашей программе, его можно удалить с помощью функции:

 

 

DeleteCriticalSection(&cs);

 

 

Это приведет к освобождению всех ресурсов системы, задействованных для поддержки объекта критический раздел.

 

 

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

 

 

Использование событий

 

 

Альтернативным вариантом синхронизации является использование событий. Объект событие может быть либо свободным (signaled) или установленным (set), либо занятым (non-signaled) или сброшенным (reset). Вы можете создать объект "событие" с помощью функции:


 

 

hEvent = CreateEvent(&sa, fManual, fInitial, pszName);

 

 

Первый параметр - указатель на структуру SECURITY_ATTRIBUTES, устанавливающую параметры использования события потоками и процессами.

 

 

Параметр fInitial устанавливается в TRUE, если вы хотите, чтобы объект Событие был изначально свободным, или в FALSE, чтобы он был занятым.

 

 

Параметр pszName определяет имя события. По этому имени разные процессы могут использовать одно событие.

 

 

Для того, чтобы сделать свободным существующий объект Событие, надо вызвать функцию:

 

 

SetEvent(hEvent);

 

 

Чтобы сделать объект Событие занятым, вызывается функция:

 

 

ResetEvent(hEvent);

 

 

Для синхронизации используется функция:

 

 

WaitForSingleObject(hEvent, dwTimeOut);

 

 

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

 

 

Если параметр fManual имеет значение FALSE при вызове функции CreateEvent, то объект Событие автоматически становится занятым, когда осуществляется возврат из функции WaitForSingleObject. Эта особенность позволяет избежать использования функции ResetEvent.

 

 

К вопросу о переменных

 

 

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


– 79 –


 

 

ЛЕКЦИЯ 8. МНОГООКОННЫЙ ИНТЕРФЕЙС

 

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

 

 

Многооконный интерфейс (Multiple Document Interface, MDI) является спецификацией для приложений, которые обрабатывают документы в Microsoft Windows. Спецификация описывает структуру окон и пользовательский интерфейс, который позволяет пользователю работать с несколькими документами внутри одного приложения (с документами в текстовом процессоре или с таблицами в программе электронных таблиц). Точно также, как Windows поддерживает несколько окон приложений на одном экране, приложение MDI поддерживает несколько окон документов в одной рабочей области. Первым приложением MDI для Windows была первая версия Microsoft Excel. Microsoft Word for Windows и Microsoft Access являются приложениями MDI. Хотя спецификация MDI была введена, уже начиная с Windows 2, в то время писать приложения MDI было трудно, и от программиста требовалось большая очень сложная работа. Однако, начиная с Windows 3, большая часть этой работы была сделана. Windows 95 добавляет только одну новую функцию и одно новое сообщение к набору функций, структурам данных и сообщениям, которые существуют специально для упрощения создания приложений MDI.

 

 

Элементы MDI

 

 

Главное окно приложения программы MDI обычно: в нем имеется строка заголовка, меню, рамка изменения размера, значок системного меню и значки свертывания. Рабочая область, однако, не используется непосредственно для вывода выходных данных программы. В этой рабочей области может находиться несколько дочерних окон, в каждом из которых отображается какой - документ.

 

 

Эти дочерние окна выглядят совершенно так же, как обычные окна приложений. В них имеется строка заголовка, рамка изменения размера, значок системного меню, значки свертывания и, возможно, полосы прокрутки. Однако, ни в одном из окон документов нет меню. Меню главного окна приложения относится и к окнам документов.

 

 

В каждый конкретный момент времени только одно окно документа активно (об этом говорит выделенная подсветкой строка заголовка) и находится над всеми остальными окнами документов. Все дочерние окна документов находятся только в рабочей области главного окна приложения и никогда не выходят за ее границы.

 

 

Поначалу, MDI для Windows-программиста кажется совершенно понятным. Все, что нужно сделать — это создать для каждого документа окно WS_CHILD, делая главное окно приложения родительским окном для окна документа. Но при более близком знакомстве с приложением MDI, таким как Microsoft Excel, обнаруживаются определенные трудности, требующие сложного программирования. Например:

 

 

Окно документа MDI может быть свернуто. Соответствующий значок выводится в нижней части рабочей области (правило: в приложении MDI для главного окна приложения и для каждого типа окна документа будут использоваться разные значки).

 

 

Окно документа MDI может быть развернуто. В этом случае строка заголовка окна документа (обычно используется для вывода в окне имени файла документа) исчезает, и имя файла оказывается присоединенным к имени приложения в строке заголовка окна приложения. Значок системного меню окна документа становится первым пунктом строки основного меню окна приложения. Значок для восстановления размера окна документа становится последним пунктом строки основного меню и оказывается крайним справа.

 

 

Системные быстрые клавиши для закрытия окна документа те же, что и для закрытия главного окна, за исключением того, что клавиша <Ctrl> используется вместо клавиши <Alt>. Таким образом, комбинация <Alt>+<F4> закрывает окно приложения, а комбинация <Ctrl>+<F4> закрывает окно документа. Вдобавок к остальным быстрым клавишам, комбинация <Ctrl>+<F6> позволяет переключаться между


– 81 –

 

 

дочерними окнами документов активного приложения MDI. Комбинация <Alt>+<Spacebar>, как обычно, вызывает системное меню главного окна. Комбинация <Alt>+<-> () вызывает системное меню активного дочернего окна документа.

 

 

При использовании клавиш управления курсором для перемещения по пунктам меню, обычно происходит переход от системного меню к первому пункту строки меню. В приложении MDI порядок перехода изменяется: от системного меню приложения — к системному меню активного документа — и далее к первому пункту строки меню.

 

 

Если приложение имеет возможность поддерживать несколько типов дочерних окон (электронные таблицы и диаграммы в Microsoft Excel), то меню должно отражать операции, ассоциированные с каждым типом документа. Для этого требуется, чтобы программа изменяла меню программы, когда становится активным окно документа другого типа. Кроме этого, при отсутствии окна документа, в меню должны быть представлены только операции, связанные с открытием нового документа.

 

 

В строке основного меню имеется пункт Window. По соглашению, он является последним пунктом строки основного меню, исключая Help. В подменю Window обычно имеются опции для упорядочивания окон документов внутри рабочей области. Окна документов можно расположить cascaded, начиная от верхнего левого угла, или tiled так, что окно каждого документа будет полностью видимо.

 

 

Кроме того, в этом подменю имеется перечень всех окон документов. При выборе одного из окон документов, оно выходит на передний план.

 

 

Все эти аспекты MDI поддерживаются в Windows.

 

 

Windows и MDI

 

 

При знакомстве с поддержкой MDI в Windows требуется новая терминология. Окно приложения в целом называется главным окном (frame window). Также как в традиционной программе для Windows, это окно имеет стиль WS_OVERLAPPEDWINDOW.

 

 

Приложение MDI также создает окно - (client window) на основе предопределенного класса окна MDICLIENT. Окно создается с помощью вызова функции CreateWindow с использованием этого класса окна и стиля WS_CHILD. Последним параметром функции CreateWindow является указатель на небольшую структуру типа CLIENTCREATESTRUCT. Это окно - охватывает всю рабочую область главного окна и обеспечивает основную поддержку MDI. Цветом окна - является системный цвет COLOR_APPWORKSPACE.


 

Рис. 11.1. Иерархия родительских и дочерних окон приложения MDI в Windows

 

 

Окна документов называются дочерними окнами (child windows). Эти окна создаются путем инициализации структуры типа MDICREATESTRUCT и посылки окну - сообщения WM_MDICREATE с указателем на эту структуру.

 

 

Окна документов являются дочерними окнами окна-клиента, которое, в свою очередь, является дочерним окном главного окна. Эта иерархия показана на рис. 11.1

 

 

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

 

 

Для поддержки MDI в Windows имеется один класс окна, пять функций, две структуры данных и двенадцать сообщений. О классе окна MDICLIENT и структурах данных CLIENTCREATESTRUCT и MDICREATESTRUCT уже упоминалось. Две из пяти функций заменяют в приложениях MDI функцию DefWindowPro c: вместо вызова функции DefWindowProc для всех необрабатываемых сообщений, оконная процедура главного окна вызывает функцию DefFramePro c, а оконная процедура дочернего окна вызывает функцию DefMDIChildPro c. Другая характерная функция MDI TranslateMDISysAccel используется также, как функция TranslateAccelerato r, о которой рассказывалось в Лекции 10.

 

 

Если в дочерних окнах MDI выполняются протяженные во времени операции, рассмотрите возможность их запуска в отдельных потоках. Это позволит пользователю покинуть дочернее окно и продолжить работу в другом окне, пока первое дочернее окно решает свою задачу в фоновом режиме. В Windows специально для этой цели имеется новая функция CreateMDIWindo w. Поток вызывает функцию CreateMDIWindow для создания дочернего окна MDI; таким образом окно действует исключительно внутри контекста потока. В программе, имеющей один поток, для создания дочернего окна функция CreateMDIWindow не требуется, поскольку то же самое выполняет сообщение WM_MDICREATE.

 

 

Приведем пример - потоковую программы, в которой будет показано девять из двенадцати сообщений MDI. Эти сообщения имеют префикс WM_MDI. Главное окно посылает одно из этих сообщений окну -для выполнения операции над дочерним окном или для получения информации о дочернем окне (главное окно посылает сообщение WM_MDICREATE окну - для создания дочернего окна). Исключение


– 83 –

 

 

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

 

 

Пример программы

 

 

#include <windows.h>

 

 

#include< stdlib.h>

 

 

#include "mdidemo.h"

 

 

// Предварительное объявление функций

 

 

LRESULT CALLBACK FrameWndProc (HWND, UINT, WPARAM, LPARAM);

 

 

BOOL CALLBACK CloseEnumProc(HWND, LPARAM);

 

 

LRESULT CALLBACK HelloWndProc (HWND, UINT, WPARAM, LPARAM);

 

 

LRESULT CALLBACK RectWndProc (HWND, UINT, WPARAM, LPARAM);

 

 

// Глобальные переменнве

 

 

char szFrameClass[] = "MdiFrame";

 

 

char szHelloClass[] = "MdiHelloChild";

 

 

char szRectClass[] = "MdiRectChild";

 

 

HINSTANCE hInst;

 

 

HMENU hMenuInit, hMenuHello, hMenuRect;

 

 

HMENU hMenuInitWindow, hMenuHelloWindow, hMenuRectWindow;

 

 

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,

 

 

PSTR szCmdLine, int iCmdShow)

 

 

{

 

 

HACCEL hAccel;

 

 

HWND hwndFrame, hwndClient;


 

 

MSG msg;

 

 

WNDCLASSEX wndclass;

 

 

hInst = hInstance;

 

 

if(!hPrevInstance)

 

 

{

 

 

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

 

 

wndclass.cbSize = sizeof(wndclass);

 

 

wndclass.style = CS_HREDRAW | CS_VREDRAW;

 

 

wndclass.lpfnWndProc = FrameWndProc;

 

 

wndclass.cbClsExtra = 0;

 

 

wndclass.cbWndExtra = 0;

 

 

wndclass.hInstance = hInstance;

 

 

wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);

 

 

wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);

 

 

wndclass.hbrBackground =(HBRUSH)(COLOR_APPWORKSPACE + 1);

 

 

wndclass.lpszMenuName = NULL;

 

 

wndclass.lpszClassName = szFrameClass;

 

 

wndclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

 

 

RegisterClassEx(&wndclass);

 

 

// Регистрация класса окна, в котором выводится фраза "Привет, мир!"

 

 

wndclass.cbSize = sizeof(wndclass);

 

 

wndclass.style = CS_HREDRAW | CS_VREDRAW;

 

 

wndclass.lpfnWndProc = HelloWndProc;

 

 

wndclass.cbClsExtra = 0;

 

 

wndclass.cbWndExtra = sizeof(HANDLE);

 

 

wndclass.hInstance = hInstance;

 

 

wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);


– 85 –

 

 

wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);

 

 

wndclass.hbrBackground =(HBRUSH) GetStockObject(WHITE_BRUSH);

 

 

wndclass.lpszMenuName = NULL;

 

 

wndclass.lpszClassName = szHelloClass;

 

 

wndclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

 

 

RegisterClassEx(&wndclass);

 

 

// Регистрация класса окна, в котором выводится прямоугольник

 

 

wndclass.cbSize = sizeof(wndclass);

 

 

wndclass.style = CS_HREDRAW | CS_VREDRAW;

 

 

wndclass.lpfnWndProc = RectWndProc;

 

 

wndclass.cbClsExtra = 0;

 

 

wndclass.cbWndExtra = sizeof(HANDLE);

 

 

wndclass.hInstance = hInstance;

 

 

wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);

 

 

wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);

 

 

wndclass.hbrBackground =(HBRUSH) GetStockObject(WHITE_BRUSH);

 

 

wndclass.lpszMenuName = NULL;

 

 

wndclass.lpszClassName = szRectClass;

 

 

wndclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

 

 

RegisterClassEx(&wndclass);

 

 

}

 

 

// Формирование описателей меню для разных окон

 

 

hMenuInit = LoadMenu(hInst, "MdiMenuInit");

 

 

hMenuHello = LoadMenu(hInst, "MdiMenuHello");

 

 

hMenuRect = LoadMenu(hInst, "MdiMenuRect");

 

 

hMenuInitWindow = GetSubMenu(hMenuInit, INIT_MENU_POS);


 

 

hMenuHelloWindow = GetSubMenu(hMenuHello, HELLO_MENU_POS);

 

 

hMenuRectWindow = GetSubMenu(hMenuRect, RECT_MENU_POS);

 

 

// Чтение горячих клавиш

 

 

hAccel = LoadAccelerators(hInst, "MdiAccel");

 

 

// Создание главного окна

 

 

hwndFrame = CreateWindow(szFrameClass, "MDI Demonstration",

 

 

WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,

 

 

CW_USEDEFAULT, CW_USEDEFAULT,

 

 

CW_USEDEFAULT, CW_USEDEFAULT,

 

 

NULL, hMenuInit, hInstance, NULL);

 

 

hwndClient = GetWindow(hwndFrame, GW_CHILD);

 

 

ShowWindow(hwndFrame, iCmdShow);

 

 

UpdateWindow(hwndFrame);

 

 

// Цикл обработки сообщений

 

 

while(GetMessage(&msg, NULL, 0, 0))

 

 

{

 

 

if(!TranslateMDISysAccel(hwndClient,& msg) &&

 

 

!TranslateAccelerator(hwndFrame, hAccel, &msg))

 

 

{

 

 

TranslateMessage(&msg);

 

 

DispatchMessage(&msg);

 

 

}

 

 

}

 

 

// Удаление меню

 

 

DestroyMenu(hMenuHello);


– 87 –

 

 

DestroyMenu(hMenuRect);

 

 

return msg.wParam;

 

 

}

 

 

// оконная процедура главного окна

 

 

LRESULT CALLBACK FrameWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)

 

 

{

 

 

static HWND hwndClient;

 

 

CLIENTCREATESTRUCT clientcreate;

 

 

HWND hwndChild;

 

 

MDICREATESTRUCT mdicreate;

 

 

switch(iMsg)

 

 

{

 

 

case WM_CREATE: // Создание клиентского окна

 

 

clientcreate.hWindowMenu = hMenuInitWindow;

 

 

clientcreate.idFirstChild = IDM_FIRSTCHILD;

 

 

hwndClient = CreateWindow("MDICLIENT", NULL,

 

 

WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE,

 

 

0, 0, 0, 0, hwnd,(HMENU) 1, hInst, (LPSTR) &clientcreate);

 

 

return 0;

 

 

case WM_COMMAND:

 

 

switch(wParam)

 

 

{

 

 

case IDM_NEWHELLO: // Создание дочернего окна, в котором выводится фраза "Привет, мир!"

 

 

mdicreate.szClass = szHelloClass;

 

 

mdicreate.szTitle = "Hello";

 

 

mdicreate.hOwner = hInst;

 

 

mdicreate.x = CW_USEDEFAULT;


 

 

mdicreate.y = CW_USEDEFAULT;

 

 

mdicreate.cx = CW_USEDEFAULT;

 

 

mdicreate.cy = CW_USEDEFAULT;

 

 

mdicreate.style = 0;

 

 

mdicreate.lParam = 0;

 

 

hwndChild =(HWND) SendMessage(hwndClient, WM_MDICREATE, 0,

 

 

(LPARAM)(LPMDICREATESTRUCT)& mdicreate);

 

 

return 0;

 

 

case IDM_NEWRECT: // Создание дочернего окна, в котором выводится прямоугольник

 

 

mdicreate.szClass = szRectClass;

 

 

mdicreate.szTitle = "Rectangles";

 

 

mdicreate.hOwner = hInst;

 

 

mdicreate.x = CW_USEDEFAULT;

 

 

mdicreate.y = CW_USEDEFAULT;

 

 

mdicreate.cx = CW_USEDEFAULT;

 

 

mdicreate.cy = CW_USEDEFAULT;

 

 

mdicreate.style = 0;

 

 

mdicreate.lParam = 0;

 

 

hwndChild =(HWND) SendMessage(hwndClient, WM_MDICREATE, 0,

 

 

(LPARAM)(LPMDICREATESTRUCT)& mdicreate);

 

 

return 0;

 

 

case IDM_CLOSE: // Закрыти активного окна

 

 

hwndChild =(HWND) SendMessage(hwndClient, WM_MDIGETACTIVE, 0, 0);

 

 

if (SendMessage(hwndChild, WM_QUERYENDSESSION, 0, 0))

 

 

SendMessage(hwndClient, WM_MDIDESTROY, (WPARAM) hwndChild, 0);

 

 

return 0;

 

 

case IDM_EXIT: // Выход из программы

 

 

SendMessage(hwnd, WM_CLOSE, 0, 0);


– 89 –

 

 

return 0;

 

 

//Сообщения о сортироке окон

 

 

case IDM_TILE:

 

 

SendMessage(hwndClient, WM_MDITILE, 0, 0);

 

 

return 0;

 

 

case IDM_CASCADE:

 

 

SendMessage(hwndClient, WM_MDICASCADE, 0, 0);

 

 

return 0;

 

 

case IDM_ARRANGE:

 

 

SendMessage(hwndClient, WM_MDIICONARRANGE, 0, 0);

 

 

return 0;

 

 

case IDM_CLOSEALL: // Закрыть все дочерние окна

 

 

EnumChildWindows(hwndClient,& CloseEnumProc, 0);

 

 

return 0;

 

 

default:

 

 

hwndChild =(HWND) SendMessage(hwndClient, WM_MDIGETACTIVE, 0, 0);

 

 

if(IsWindow(hwndChild))

 

 

SendMessage(hwndChild, WM_COMMAND, wParam, lParam);

 

 

break; //...and then to DefFrameProc

 

 

}

 

 

break;

 

 

case WM_QUERYENDSESSION:

 

 

case WM_CLOSE: // Attempt to close all children

 

 

SendMessage(hwnd, WM_COMMAND, IDM_CLOSEALL, 0);

 

 

if(NULL!= GetWindow(hwndClient, GW_CHILD))

 

 

return 0;

 

 

break;

 

 

case WM_DESTROY:


 

 

PostQuitMessage(0);

 

 

return 0;

 

 

}

 

 

// Использоваие DefFrameProc(not вместо DefWindowProc

 

 

return DefFrameProc(hwnd, hwndClient, iMsg, wParam, lParam);

 

 

}

 

 

// Функция закрытия окон

 

 

BOOL CALLBACK CloseEnumProc(HWND hwnd, LPARAM lParam)

 

 

{

 

 

if(GetWindow(hwnd, GW_OWNER)) return 1;

 

 

SendMessage(GetParent(hwnd), WM_MDIRESTORE,(WPARAM) hwnd, 0);

 

 

if(!SendMessage(hwnd, WM_QUERYENDSESSION, 0, 0)) return 1;

 

 

SendMessage(GetParent(hwnd), WM_MDIDESTROY,(WPARAM) hwnd, 0);

 

 

return 1;

 

 

}

 

 

// Оконная процедура первого окна

 

 

LRESULT CALLBACK HelloWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)

 

 

{

 

 

static COLORREF clrTextArray[] = { RGB(0, 0, 0), RGB(255, 0, 0),

 

 

RGB(0, 255, 0), RGB(0, 0, 255),

 

 

RGB(255, 255, 255) };

 

 

static HWND hwndClient, hwndFrame;

 

 

HDC hdc;

 

 

HMENU hMenu;

 

 

LPHELLODATA lpHelloData;

 

 

PAINTSTRUCT ps;


– 91 –

 

 

RECT rect;

 

 

switch(iMsg)

 

 

{

 

 

case WM_CREATE:

 

 

// Save some window handles

 

 

hwndClient = GetParent(hwnd);

 

 

hwndFrame = GetParent(hwndClient);

 

 

return 0;

 

 

case WM_PAINT:

 

 

// Рисование

 

 

hdc = BeginPaint(hwnd, &ps);

 

 

GetClientRect(hwnd,& rect);

 

 

DrawText(hdc, " Привет, Мир!", -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);

 

 

EndPaint(hwnd,& ps);

 

 

return 0;

 

 

case WM_MDIACTIVATE:

 

 

// Установка нового меню

 

 

if(lParam ==(LPARAM) hwnd)

 

 

SendMessage(hwndClient, WM_MDISETMENU,

 

 

(WPARAM) hMenuHello,(LPARAM) hMenuHelloWindow);

 

 

DrawMenuBar(hwndFrame);

 

 

return 0;

 

 

case WM_QUERYENDSESSION:

 

 

case WM_CLOSE:

 

 

if(IDOK!= MessageBox(hwnd, "OK to close window?", "Hello",

 

 

MB_ICONQUESTION | MB_OKCANCEL))

 

 

return 0;

 

 

break;


 

 

case WM_DESTROY:

 

 

return 0;

 

 

}

 

 

return DefMDIChildProc(hwnd, iMsg, wParam, lParam);

 

 

}

 

 

// Оконная процедура второго дочернего окна

 

 

LRESULT CALLBACK RectWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)

 

 

{

 

 

static HWND hwndClient, hwndFrame;

 

 

HBRUSH hBrush;

 

 

HDC hdc;

 

 

LPRECTDATA lpRectData;

 

 

PAINTSTRUCT ps;

 

 

int xLeft, xRight, yTop, yBottom;

 

 

short nRed, nGreen, nBlue;

 

 

switch(iMsg)

 

 

{

 

 

case WM_CREATE:

 

 

// Save some window handles

 

 

hwndClient = GetParent(hwnd);

 

 

hwndFrame = GetParent(hwndClient);

 

 

return 0;

 

 

case WM_PAINT: // Рисование

 

 

hdc = BeginPaint(hwnd, &ps);

 

 

Rectangle(hdc,10,10,200,200);

 

 

EndPaint(hwnd,& ps);

 

 

return 0;


– 93 –

 

 

case WM_MDIACTIVATE: // Установка меню.

 

 

if(lParam ==(LPARAM) hwnd)

 

 

SendMessage(hwndClient, WM_MDISETMENU,(WPARAM) hMenuRect,

 

 

(LPARAM) hMenuRectWindow);

 

 

else

 

 

SendMessage(hwndClient, WM_MDISETMENU,(WPARAM) hMenuInit,

 

 

(LPARAM) hMenuInitWindow);

 

 

DrawMenuBar(hwndFrame);

 

 

return 0;

 

 

case WM_DESTROY:

 

 

return 0;

 

 

}

 

 

// Пересылка необработанных сообщений в DefMDIChildProc

 

 

return DefMDIChildProc(hwnd, iMsg, wParam, lParam);

 

 

}

 

 

Рис. 11.2 Текст программы MDIDEMO

 

 

Программа MDIDEMO поддерживает два типа предельно простых окон документов: в одном в центре рабочей области выводится фраза "Hello, World!", а во втором отображается прямоугольник. С каждым из этих двух типов окон документов связано свое меню.

 

 

Три меню

 

 

Начнем анализ программы с файла описания ресурсов MDIDEMO.RC. В нем определяются три шаблона меню, используемые в программе. Программа выводит на экран меню MdiMenuInit при отсутствии окон документов. Это меню просто позволяет создать новый документ или завершить программу. Меню MdiMenuHello связано с окном документа, в котором выводится фраза "Hello, World!". Подменю File позволяет открыть новый документ любого типа, закрыть активный документ и завершить программу. В подменю Window имеются опции для упорядочивания окон документов в каскадном или мозаичном виде, упорядочивания значков документов и закрытия всех окон. В этом подменю также имеется список всех открытых окон документов.

 

 

Меню MdiMenuRect связано с документом с прямоугольником. В заголовочном файле MDIDEMO.Н все идентификаторы меню определяются как три константы:

 

 

#define INIT_MENU_POS 0


 

 

#define HELLO_MENU_POS 2

 

 

#define RECT_MENU_POS 1

 

 

Эти идентификаторы задают положение подменю Window в каждом из трех шаблонов меню. Эта информация необходима программе, чтобы информировать окно -клиент о том, когда должен появиться список документов. Конечно, в меню MdiMenuInit нет подменю Window, поэтому в файле обозначено, что список должен быть присоединен к первому подменю. Однако, фактически здесь список никогда не появится. (он нужен станет ясно при дальнейшем анализе программы.)

 

 



Поделиться:


Последнее изменение этой страницы: 2017-02-05; просмотров: 369; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 54.221.69.42 (1.234 с.)