47816 (597361), страница 5
Текст из файла (страница 5)
if ( !hPrevInstance ) {
WC.style= NULL;
WC.lpfnWndProc= WinProc;
WC.cbClsExtra= NULL;
WC.cbWndExtra= NULL;
WC.hInstance= hInstance;
WC.hIcon= LoadIcon( NULL, IDI_APPLICATION );
WC.hCursor= LoadCursor( NULL, IDC_ARROW );
WC.hbrBackground= GetStockObject( WHITE_BRUSH );
WC.lpszMenuName= NULL;
WC.lpszClassName= "Hello application";
if ( !RegisterClass( &WC ) ) return NULL;
}
Эта операция обычно выполняется в отдельной процедуре (init_instance в 1a.cpp) так как структура WNDCLASS используется однократно, только для вызова функции RegisterClass, после чего ее можно не держать в стеке.
Структура WNDCLASS содержит следующие поля:
Поле style содержит комбинацию CS_xxx констант, задающих некоторые характеристики класса. Часто этот параметр равен NULL. Для более подробного знакомства с этим полем рекомендуется посмотреть описание функции RegisterClass и возможные стили класса.
Например, стиль CS_HREDRAW говорит о том, что окно должно перерисовываться целиком при изменении его горизонтального размера, CS_VREDRAW — при изменении вертикального размера, CS_DBLCLK — окно будет реагировать на двойные нажатия на клавишу мыши. Особенно надо отметить стиль CS_GLOBALCLASS. Обычно зарегистрированный класс используется только лишь данным приложением (и всеми его копиями), но недоступен для других приложений. Стиль CS_GLOBALCLASS разрешает использование класса другими приложениями.
Если Вы захотите объединить несколько стилей класса, используйте оператор ПОБИТОВОЕ ИЛИ, например: CS_HREDRAW|CS_VREDRAW|CS_DBLCLK.
lpfnWndProc является указателем на оконную функцию. Вы не можете указать его 0, эта функция должна быть написана Вами.
cbClsExtra, cbWndExtra. При регистрации класса окна Windows создает специальный блок, содержащий информацию о классе; В некоторых случаях бывает удобно включить в эту структуру свои данные (их смогут использовать все окна, принадлежащие этому классу — даже окна разных приложений).
Для этого Windows может зарезервировать специальное добавочное пространство в этом блоке, размер этого пространства указывается параметром cbClsExtra. Это пространство может быть использовано вами по своему усмотрению, поэтому вы должны задать его размер. Если Вы не собираетесь использовать это пространство, укажите его размер 0 (NULL).
Позже, при создании окна, Windows создаст другой блок, описывающий окно, и в нем выделит дополнительное пространство размером cbWndExtra байт. Оно также предназначено для использования Вами. Если оно Вам не требуется, укажите размер 0 (NULL). В таких полях удобно хранить данные, уникальные для каждого окна — скажем, хендл редактируемого файла, если это окно редактора.
Рисунок 2. Структуры данных, используемые Windows для описания окон.
Вы можете использовать при желании двенадцать функций для чтения/записи данных, находящихся в структурах описания окна и класса:
UINT GetWindowWord( hWnd, nIndex );
UINT SetWindowWord( hWnd, nIndex, wNewValue );
LONG GetWindowLong( hWnd, nIndex );
LONG SetWindowLong( hWnd, nIndex, dwNewValue );
int GetWindowText( hWnd, lpWinName, nMaxCount);
int GetWindowTextLength( hWnd );
void SetWindowText( hWnd, lpszWinName );
“Текст окна” (...WindowText...) является обычно заголовком окна, а для окон специального вида (например, кнопки) — текстом, написанном в этом окне.
UINT GetClassWord( hWnd, nIndex );
UINT SetClassWord( hWnd, nIndex, wNewValue );
LONG GetClassLong( hWnd, nIndex );
LONG SetClasslong( hWnd, nIndex, dwNewValue );
int GetClassName( hWnd, lpClassName, nMaxCount );
Поле hInstance, конечно, содержит хендл копии приложения, регистрирующего данный класс окон. Он будет использоваться Windows для связи оконной функции с копией приложения, содержащего оконную функцию.
Следующие три поля (hIcon, hCursor, hbrBackground) пояснить будет посложнее — для этого надо будет разобраться с GDI (hbrBackground) и ресурсами (hIcon, hCursor), что будет сделано в соответствующих разделах. Поэтому пока что мы сделаем несколько замечаний и рассмотрим простейший случай.
hIcon — это хендл пиктограммы (иконки), которая будет выводиться вместо окна в минимизированном состоянии. Функция LoadIcon находит соответствующую пиктограмму и возвращает ее хендл. В нашем примере LoadIcon использует стандартную пиктограмму (первый параметр равен NULL), которая используется по умолчанию для представления приложения (второй параметр равен IDI_APPLICATION).
hCursor является, аналогично hIcon, хендлом курсора. Это курсор мыши, который будет отображаться при перемещении мыши через окно. Требуемый курсор загружается функцией LoadCursor, применение которой похоже на функцию LoadIcon. Для стандартных курсоров существуют следующие имена: IDC_ARROW, IDC_CROSS, IDC_IBEAM, IDC_ICON, IDC_SIZE, IDC_SIZENESW, IDC_SIZES, IDC_SIZENWSE, IDC_SIZEWE, IDC_UPARROW и IDC_WAIT. Наиболее часто употребляемый курсор IDC_ARROW.
Третий хендл — хендл кисти — hbrBackground. Применение кисти существенно отличается от пиктограммы и курсора, поэтому она задается иным способом. Функция GetStockObject возвращает хендл заранее созданного Windows стандартного объекта. Мы используем GetStockObject для получения хендла “белой кисти” WHITE_BRUSH. Помимо хендлов кистей эта функция может возвращать хендлы других объектов. В примере 1a.cpp функция GetStockObject не применялась — вместо хендла кисти разрешено указывать специальную константу, обозначающую системный цвет (в примере — COLOR_WINDOW). Для того, что бы система могла отличить хендл от цвета, требуется, что бы к нему была прибавлена 1 (0 означает неверный хендл, являясь в то же время корректным номером цвета).
Следует отметить, что кисть WHITE_BRUSH вовсе не обязательно имеет белый цвет. Просто эта кисть обычно используется для закраски фона окна, часто это действительно белый цвет, но он может быть изменен при настройке цветов Windows.
Указатель на строку lpszMenuName. Если Ваше окно имеет связанное с ним меню, то Вы можете указать имя этого меню для его автоматического использования. Как это делается мы рассмотрим несколько позже. В данном примере предполагается, что меню нет — lpszMenuName равен NULL.
Последний параметр lpszClassName является именем регистрируемого класса окна. Рекомендуется включать в это имя название задачи, так как это уменьшает вероятность совпадения имени данного класса с уже зарегистрированным классом другого приложения.
ВНИМАНИЕ! Если Вы применяете русские символы в именах меню, класса и др., то Вам надо быть очень осторожными — стандартные шрифты нелокализованных для России версий Windows 3.x не содержат русских символов, и, помимо этого, компиляторы могут не воспринимать русский текст, особенно некоторые его буквы (как, например, букву “я”, код которой равен 255). Дополнительно могут встретиться осложнения из–за того, что среда программирования в Windows может использовать собственные нерусифицированные шрифты, или из–за того, что DOS–программы используют иную кодировку русских символов, чем Windows–приложения (если Вы используете какие–либо DOS–средства для разработки программ, либо Ваше приложение будет использовать DOS–программы).
Если функция RegisterClass вернула не 0, то класс успешно зарегистрирован. Этот зарегистрированный класс будет существовать до тех пор, пока приложение активно, или пока Вы не вызовете функцию
UnregisterClass( lpszClassName, hInstance )
для уничтожения данного класса. Если вы собираетесь вызвать функцию UnregisterClass, то надо убедиться что нет ни одного окна, принадлежащего данному классу. Эта функция обычно применяется для глобальных классов (со стилем CS_GLOBALCLASS).
Создание и отображение окна
После регистрации класса мы можем создавать окна. Делается это с помощью процедуры CreateWindow. Эта функция создает окно и возвращает его хендл. Если создание окна почему–либо невозможно, то возвращается значение NULL. Рассмотрим параметры этой функции:
HWND CreateWindow(
lpszClassName, lpszWindowName, dwStyle,
nX, nY, nWidth, nHeight,
hWndParent, hMenu, hInstance, lpParam
);
lpszClassName задает имя класса окна. По этому имени Windows находит описание класса окон и, соответственно, оконную функцию, которая будет вызываться для обработки сообщений, посылаемых окну. Такой механизм связывания окна с функцией (через имя класса) позволяет определять оконную функцию в одном приложении, а использовать в другом.
lpszWindowName задает заголовок окна. Этот заголовок размещается в верхней части окна. В некоторых случаях этот текст используется иначе — например, если окно — "кнопка", то это текст на кнопке, или это может быть текст под пиктограммой, если окно минимизировано.
Параметр dwStyle содержит битовые флаги, задающие некоторые характеристики окна, как–то тип рамки, наличие заголовка, кнопок системного меню, минимизации, максимизации и т.д. Признаки окна называются стилями и описаны в windows.h с префиксом WS_. Вы должны объединять отдельные стили с помощью операции ПОБИТОВОЕ ИЛИ: WS_CAPTION|WS_SYSMENU.
Возможных стилей окна очень много, мы их будем рассматривать их по мере надобности. Для справок надо обратиться к описанию функции 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–интерфейса (Multiple Document Interface) — о MDI смотри в разделе, посвященном оконным системам. Этот указатель передается оконной функции при обработке сообщения, информирующего о создании окна, и используется непосредственно самой оконной функцией. Так Вы можете передать оконной функции требуемые для создания окна данные через этот параметр.
В некоторых случаях при описании стиля окна необходимо задавать дополнительные характеристики, которые не входят в состав параметра dwStyle. Так, например, Вы можете создать окно, которое всегда находится поверх всех остальных окон (always on top, topmost). Для этого используется дополнительное двойное слово стиля (extended style) и, соответственно, другая функция для создания окна:
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). Для начала следует выделить некоторый источник сообщений. Им может являться какое–либо устройство ввода (например, клавиатура, мышь), специальные устройства (типа таймера), либо какое–либо работающее приложение. Сообщения, полученные от источника сообщений, накапливаются в очереди сообщений (message queue). Далее сообщения будут извлекаться из очереди и передаваться для обработки конкретной оконной процедуре.
Для извлечения из очереди используется специальный цикл обработки сообщений (message loop), разрабатываемый для каждого приложения. В этом цикле сообщения извлекаются из очереди, определяется, какое окно (какая процедура обработки сообщений) должно это сообщение получить, при необходимости выполняются специальные действия, и только затем сообщение передается конкретной оконной процедуре. И такой процесс работает все время, пока работает приложение.
Процесс помещения сообщения в очередь и его извлечения из нее никак жестко не увязан по времени. Более того, может случиться так, что посланное сообщение вообще не будет обработано, например, если окно–получатель будет уничтожено прежде, чем успеет его обработать.















