Смекни!
smekni.com

Ознакомление с приложениями Windows (стр. 7 из 18)

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

Рисунок 3. Основные элементы окна

nX, nY эти параметры задают позицию верхнего левого угла окна относительно экрана или, для дочернего окна, относительно верхнего левого угла внутренней области родительского окна. Позиция задается в пикселях. Вместо конкретных чисел можно подставить CW_USEDEFAULT — в этом случае Windows будет определять позицию сам.

nWidth, nHeight — ширина и высота окна в пикселях. Вместо этих величин Вы можете указать CW_USEDEFAULT, для того, что бы Windows самостоятельно определил их.

hWndParent этот параметр является хендлом родительского окна. Если Вы указали NULL, то данное окно не является дочерним (используемым)[9], а если Вы задали хендл другого окна, то вновь создаваемое окно будет дочерним (используемым) по отношению к указанному.

hMenu обычно задает хендл меню, используемого окном. Однако здесь есть несколько тонкостей:

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

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

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

hInstance, как и раньше, является хендлом копии приложения, использующего данное окно. Обратите внимание на то, что такой же хендл при регистрации класса указывает на копию приложения, содержащую оконную функцию, а здесь хендл указывает на копию приложения, использующего данное окно (и, в Windows API, может отличаться).

lpParam. Как правило, этот параметр равен NULL. Он используется сравнительно редко, обычно если окно является окном MDI–интерфейса (MultipleDocumentInterface) — о MDI смотри в разделе, посвященном оконным системам. Этот указатель передается оконной функции при обработке сообщения, информирующего о создании окна, и используется непосредственно самой оконной функцией. Так Вы можете передать оконной функции требуемые для создания окна данные через этот параметр.

В некоторых случаях при описании стиля окна необходимо задавать дополнительные характеристики, которые не входят в состав параметра dwStyle. Так, например, Вы можете создать окно, которое всегда находится поверх всех остальных окон (alwaysontop, topmost). Для этого используется дополнительное двойное слово стиля (extendedstyle) и, соответственно, другая функция для создания окна:

HWND CreateWindowEx(
dwExStyle, lpszClassName, lpszWindowName, dwStyle,
nX, nY, nWidth, nHeight,
hWndParent, hMenu, hInstance, lpParam
);

Эта функция отличается только наличием еще одного параметра dwExStyle, задающего дополнительные стили окна. Для задания этих стилей используются мнемонические имена с префиксом WS_EX_..., например WS_EX_TOPMOST или WS_EX_TRANSPARENT.

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

Однако в WinMain не рекомендуется создавать сразу видимое окно. Это связано с тем, что приложение может быть запущено в начально минимизированном состоянии. Как надо отобразить окно мы можем узнать через аргумент nCmdShow функции WinMain, но использовать его функцией CreateWindow сложно. Поэтому окно создается невидимым и затем отображается в требуемом нам виде:

ShowWindow( hWnd, nCmdShow );
UpdateWindow( hWnd );

Функция ShowWindow указывает окну, в каком виде оно должно быть отображено на экране. В функции WinMain параметр nCmdShow берется из переданных аргументов — он определяет, каким образом пользователь хочет запустить это приложение. Во время выполнения функции ShowWindow на экране отображается рамка и заголовок окна в нужном месте и требуемого размера.

Во время выполнения функции UpdateWindow отображается внутренняя область окна. Определимся, что внутренняя область окна иногда называется клиентной (client), а рамка, заголовок, или заменяющая все пиктограмма называются внешней областью, обрамлением (frame) или неклиентной (non–client) областью окна. Для отображения внутренней области окна приложение получает специальное сообщение, обработчик которого выполняет требуемое рисование.

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

Сейчас нам придется обзорно рассмотреть механизм передачи сообщений в Windows 3.x (реально он гораздо сложнее, особенно в Windows–95 или Windows NT). Для начала следует выделить некоторый источник сообщений. Им может являться какое–либо устройство ввода (например, клавиатура, мышь), специальные устройства (типа таймера), либо какое–либо работающее приложение. Сообщения, полученные от источника сообщений, накапливаются в очереди сообщений (messagequeue). Далее сообщения будут извлекаться из очереди и передаваться для обработки конкретной оконной процедуре.

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

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

Рисунок 4. Обработка посланных сообщений в Windows

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

Этот процесс называется посылкой (post) сообщений, так как посылка сообщения напоминает посылку письма: посылающий сообщение указывает адресата, отправляет сообщение и больше о нем не беспокоится. Отправитель не знает, когда точно его сообщение получит адресат. Такой способ обработки сообщений часто называется асинхронным.

Извлечение сообщений из очереди приложения и направление их соответствующим окнам осуществляет цикл обработки сообщений, обычно входящий в функцию WinMain. Этот процесс выполняется в несколько приемов:

· сообщение выбирается из очереди с помощью функции GetMessage или PeekMessage

· затем сообщение транслируется с помощью функции TranslateMessage[10] (одно сообщение может порождать последовательность других или заменяться, как, например, происходит для сообщений клавиатуры WM_KEYDOWN). Часто трансляция состоит из вызова более чем одной функции, сюда могут добавляться специальные средства трансляции акселераторов и немодальных диалогов (об этом позже).

· и только после этого оно направляется окну с помощью функции DispatchMessage (это называется диспетчеризацией)

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

MSG msg;

while ( GetMessage( &msg, NULL, NULL, NULL ) ) {
TranslateMessage( &msg );
DispatchMessage( &msg );
}

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

BOOL GetMessage( lpMsg, hWnd, uMsgFilterMin, uMsgFilterMax );

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

Какие же данные передаются одним сообщением?

typedef struct tagMSG {
HWND hwnd; // хендл окна-получателя
UINT message; // номер сообщения WM_...
WPARAM wParam; // параметр сообщения
LPARAM lParam; // параметр сообщения
DWORD time; // время поступления сообщения
POINT pt; // координаты сообщения (для сообщений мыши)
} MSG;

Поле message структуры MSG задает номер сообщения, посланного системой. Интерпретация параметров сообщения wParam и lParam зависит от самого сообщения. Для этого надо смотреть описание конкретного сообщения и обрабатывать параметры соответствующим образом. Так как в системе определено огромное количество разных сообщений, то для простоты использования применяются символические имена сообщений, задаваемыми с помощью #define в заголовочном файле. В качестве примера можно привести сообщения WM_CREATE, WM_PAINT, WM_QUIT.

hWnd указывает хендл окна, сообщения для которого будут выбираться из очереди. Если hWnd равен NULL, то будут выбираться сообщения для всех окон данного приложения, а если hWnd указывает реальное окно, то из очереди будут выбираться все сообщения, направленные этому окну или его потомкам (дочерним или используемым окнами, или их потомкам, в том числе отдаленным).

uMsgFilterMin и uMsgFilterMax обычно установлены в NULL. Вообще они задают фильтр для сообщений. GetMessage выбирает из очереди сообщения, номера (имена) которых лежат в интервале от uMsgFilterMin до uMsgFilterMax. Нулевые значения исключают фильтрацию.