95 в двух томах Том I



Pdf просмотр
страница4/41
Дата27.11.2016
Размер4.32 Mb.
Просмотров7875
Скачиваний0
ТипРеферат
1   2   3   4   5   6   7   8   9   ...   41
Файлы HELLOWIN
Два из трех файлов, необходимых для создания программы "HELLOWIN", представлены на рис. 2.1. Это make- файл HELLOWIN.MAK и файл исходного текста HELLOWIN.С.
HELLOWIN.MAK
#------------------------
# HELLOWIN.MAK make file
#------------------------ hellowin.exe : hellowin.obj
$(LINKER) $(GUIFLAGS) -OUT:hellowin.exe hellowin.obj $(GUILIBS) hellowin.obj : hellowin.c
$(CC) $(CFLAGS) hellowin.c
HELLOWIN.C
/*------------------------------------------------------------
HELLOWIN.C -- Displays "Hello, Windows 95!" in client area
(c) Charles Petzold, 1996
------------------------------------------------------------*/

23
#include
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{ static char szAppName[] = "HelloWin";
HWND hwnd;
MSG msg;
WNDCLASSEX wndclass; wndclass.cbSize = sizeof(wndclass); wndclass.style = CS_HREDRAW | CS_VREDRAW; wndclass.lpfnWndProc = WndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; 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 = szAppName; wndclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
RegisterClassEx(&wndclass); hwnd = CreateWindow( szAppName,
// window class name
"The Hello Program",
// window caption
WS_OVERLAPPEDWINDOW,
// window style
CW_USEDEFAULT,
// initial x position
CW_USEDEFAULT,
// initial y position
CW_USEDEFAULT,
// initial x size
CW_USEDEFAULT,
// initial y size
NULL,
// parent window handle
NULL,
// window menu handle hInstance,
// program instance handle
NULL
);
// creation parameters
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd); while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
} return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
RECT rect; switch(iMsg)
{ case WM_CREATE:
PlaySound("hellowin.wav", NULL, SND_FILENAME | SND_ASYNC); return 0;

24 case WM_PAINT: hdc = BeginPaint(hwnd, &ps);
GetClientRect(hwnd, &rect);
DrawText(hdc, "Hello, Windows 95!", -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
EndPaint(hwnd, &ps); return 0; case WM_DESTROY:
PostQuitMessage(0); return 0;
} return DefWindowProc(hwnd, iMsg, wParam, lParam);
}
Рис. 2.1 Программа HELLOWIN
В главе 9 вы встретите другой тип файла, широко распространенный в программировании для Windows, который называется файлом описания ресурсов (resource script) и имеет расширение .RC. Но до тех пор для большинства простых программ будут использоваться только make-файл, файл с исходным текстом на языке С, а также, возможно, заголовочный файл.
Как уже упоминалось, большая часть файла HELLOWIN.С является надстройкой, которую можно обнаружить практически в каждой программе для Windows. В действительности никто полностью не запоминает текст этой надстройки; подавляющее большинство программистов, когда пишут программу для Windows, просто копируют существующую программу и делают в ней необходимые изменения. Вы можете, используя прилагаемую дискету, поступать аналогичным образом.
Если на вашем компьютере имеется операционная система Windows 95, инсталлирован пакет Microsoft Visual C++
4.0, выполнены пакетные (batch) файлы VCVARS32.BAT, входящие в состав Visual C++, и MSC.BAT, описанный в главе 1, то введя из командной строки MS-DOS:
NMAKE HELLOWIN.MAK вы должны, таким образом, создать файл HELLOWIN.EXE.
Если все идет нормально, вы можете просто запустить программу из командной строки MS-DOS, введя:
HELLOWIN
Программа создает обычное окно приложения, как показано на рис. 2.2. В окне, в центре рабочей области, выводится текст "Hello, Windows 95!". Если у вас установлена звуковая плата, вы также услышите звуковое сообщение. (Если у вас нет звуковой платы, то чего вы собственно ждете?)
Обратите внимание, что это окно предлагает просто потрясающее количество возможностей для своих 80 строк программы. Вы можете захватить указателем мыши заголовок окна и перемещать его по всему экрану. Вы можете захватить рамку окна и изменить размеры. При изменении размеров окна программа будет автоматически перемещать строку текста "Hello, Windows 95!" в новый центр рабочей области окна. Вы можете щелкнуть на кнопке развертывания окна и увеличить HELLOWIN до размеров всего экрана. Вы можете щелкнуть на кнопке свертывания окна и стереть его с экрана. Вы можете вызвать все эти действия из системного меню. Вы также можете, чтобы завершить программу, закрыть тремя разными способами окно: выбрав соответствующую опцию из системного меню, щелкнув на кнопке закрытия окна справа в строке заголовка, или дважды щелкнув на иконке слева в строке заголовка.
Отрадно видеть, что HELLOWIN имеет все возможности обычной программы для Windows, но настроение может резко измениться, когда вы изучите исходный код, необходимый для создания этой программы. Тем не менее не пугайтесь и держите себя в руках, пока не проанализируете всю программу до конца строку за строкой.

25
Рис. 2.2 Программа HELLOWIN, работающая в Windows 95
Make-файл
Для облегчения процесса компиляции программ для Windows, вы можете пользоваться утилитой NMAKE, поставляемой вместе с Microsoft Visual C++ 4.0. Если вы захотите что-нибудь изменить в файле с исходным текстом программы HELLOWIN.С, то все, что вам нужно сделать для создания нового исполняемого файла
HELLOWIN.EXE — это запустить утилиту NMAKE так, как это было показано выше.
Make-файл состоит из одного или более разделов, каждый из которых начинается со строки, которая, в свою очередь, начинается с написания целевого файла (target file), потом идет двоеточие и далее перечисляются один или несколько файлов-источников (dependent file), из которых в итоге образуется целевой файл. За этой строкой следуют, с красной строки, одна или несколько командных строк. Эти команды собственно и предназначены для создания результирующего файла из файлов-источников. Если дата или время последней модификации любого из файлов-источников оказывается более поздней, чем дата или время последней модификации результирующего файла, то утилита NMAKE выполняет командные строки.
Обычно NMAKE модифицирует только тот результирующий файл, который расположен в первом разделе make- файла. Однако, если один из файлов-источников в другом разделе make-файла тоже оказывается результирующим файлом, то NMAKE первым модифицирует такой результирующий файл.
В make-файле HELLOWIN.MAK имеется два раздела. Если HELLOWIN.OBJ был изменен позже, чем
HELLOWIN.EXE, то командная строка первого раздела запускает компоновщик. Если HELLOWIN.C был изменен позже, чем HELLOWIN.OBJ, то командная строка второго раздела запускает компилятор языка С. Поскольку
HELLOWIN.OBJ в первом разделе является файлом-источником make-файла, а во втором разделе результирующим файлом, то утилита NMAKE, перед созданием нового файла HELLOWIN.EXE, проверит необходимость модификации HELLOWIN.OBJ. Таким образом, make-файл выполняется, фактически, снизу вверх.
Вследствие работы компилятора языка С, из файла с исходным текстом программы HELLOWIN.C создается объектный модуль HELLOWIN.OBJ. Вследствие работы компоновщика, из объектного модуля HELLOWIN.OBJ создается исполняемый файл HELLOWIN.EXE.
В главе 1 было рассказано, как макроопределения в make-файле обеспечиваются переменными окружения, задаваемыми пакетными файлами, о которых там же шла речь. Это, по большей части, подключаемый набор различных флагов компилятора и имена библиотек компоновщика, поэтому, если вы хотите изучить их подробней, вернитесь к соответствующему разделу главы 1.
Файл исходного текста программы на языке С
Вторым файлом, показанным на рис. 2.1, является файл исходного текста программы HELLOWIN.C. Определение того, что эта программа написана на языке программирования С, может отнять у вас некоторое время!

26
Перед тем как заняться деталями, давайте рассмотрим HELLOWIN.C в целом. В файле имеется только две функции: WinMain и WndProc. WinMain — это точка входа в программу. Это аналог стандартной функции main языка С. В любой программе для Windows имеется функция WinMain.
WndProc — это "оконная процедура" для окна HELLOWIN. Каждое окно, независимо от того, является ли оно большим, как главное окно приложения для Windows, или маленьким, как кнопка, имеет соответствующую оконную процедуру. Оконная процедура — это способ инкапсулирования кода, отвечающего за ввод информации
(обычно с клавиатуры или мыши) и за вывод информации на экран. Оконная процедура делает это, посылая "сообщения" окну. Не беспокойтесь о том, как именно это происходит. Позже у вас будет масса времени для того, чтобы попытаться решить эту проблему.
В HELLOWIN.C отсутствуют инструкции для непосредственного вызова WndProc: WndProc вызывается только из
Windows. Однако, в WinMain имеется ссылка на WndProc, поэтому эта функция описывается в самом начале программы, еще до определения WinMain.
Вызовы функций Windows
HELLOWIN вызывает не менее 17 функций Windows. Здесь перечислены эти функции в порядке их появления в программе (с кратким описанием каждой функции):

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

LoadCursor — загружает курсор мыши для использования в программе.

GetStockObject — получает графический объект (в этом случае для закрашивания фона окна используется кисть).

RegisterClassEx — регистрирует класс окна для определенного окна программы.

CreateWindow — создает окно на основе класса окна.

ShowWindow — выводит окно на экран.

UpdateWindow — заставляет окно перерисовать свое содержимое.

GetMessage — получает сообщение из очереди сообщений.

TranslateMessage — преобразует некоторые сообщения, полученные с помощью клавиатуры.

DispatchMessage — отправляет сообщение оконной процедуре.

PlaySound — воспроизводит звуковой файл.

BeginPaint — инициирует начало процесса рисования окна.

GetClientRect — получает размер рабочей области окна.

DrawText — выводит на экран строку текста.

EndPaint — прекращает рисование окна.

PostQuitMessage — вставляет сообщение "завершить" в очередь сообщений.

DefWindowProc — выполняет обработку сообщений по умолчанию.
Эти функции описаны в документации или системе контекстной подсказки, поставляемой с вашим компилятором, а описаны они в различных заголовочных файлах из WINDOWS.H.
Идентификаторы, написанные прописными буквами
В дальнейшем вы обратите внимание на использование в HELLOWIN.H нескольких идентификаторов, полностью написанных прописными буквами. Эти идентификаторы задаются в заголовочных файлах Windows. Некоторые из этих идентификаторов содержат двухбуквенный или трехбуквенный префикс, за которым следует символ подчеркивания:
CS_HREDRAW DT_VCENTER WM_CREATE
CS_VREDRAW IDC_ARROW WM_DESTROY
CW_USEDEFAULT
IDI_APPLICATION
WM_PAINT
DT_CENTER SND_ASYNC WS_OVERLAPPEDWINDOW
DT_SINGLELINE SND_FILENAME
Это просто числовые константы. Префикс показывает основную категорию, к которой принадлежат константы, как показано в данной таблице:

27
Префикс
Категория
CS
Опция стиля класса
IDI
Идентификационный номер иконки
IDC
Идентификационный номер курсора
WS
Стиль окна
CW
Опция создания окна
WM
Сообщение окна
SND
Опция звука
DT
Опция рисования текста
Программируя для Windows, почти никогда не нужно запоминать числовые константы. Фактически для любой числовой константы, которая используется в Windows, в заголовочных файлах имеется идентификатор.
Новые типы данных
Несколько других идентификаторов в HELLOWIN.C являются новыми типами данных; они также определяются в заголовочных файлах с помощью либо инструкций typedef, либо инструкций #define. Это изначально сделано для облегчения перевода программ для Windows с исходной 16-разрядной системы на будущие операционные системы, которые могли бы быть основаны на 32-разрядной (или иной) технологии. Эта работа не была столь гладкой и очевидной, как тогда многие думали, но идея оказалась основательной.
Иногда эти новые типы данных вполне условны. Например, тип данных UINT, использованный в качестве второго параметра WndProc — это просто беззнаковое целое, которое в Windows 95 является 32-разрядным. Тип данных
PSTR, использованный в качестве третьего параметра WinMain, является указателем на строку символов, т. е.
char*.
Другие имена менее очевидны. Например, третий и четвертый параметры WndProc определяются как WPARAM и
LPARAM соответственно. Происхождение этих имен требует небольшого экскурса в историю. Когда Windows была 16-разрядной системой, третий параметр WndProc определялся как WORD, что означало 16-разрядное
беззнаковое короткое целое, а четвертый параметр определялся как LONG, что означало 32-разрядное знаковое
длинное целое, и в этом смысл префиксов "W" и "L" у слова "PARAM". В Windows 95 имя WPARAM определяется как UINT, а LPARAM как LONG (что представляет из себя просто тип данных длинное целое языка С), следовательно оба параметра оконной процедуры являются 32-разрядными. Это может оказаться несколько неудобным, поскольку тип данных WORD в Windows 95 по-прежнему определяется как 16-разрядное беззнаковое
короткое целое, следовательно префикс "W" у слова "PARAM" создает некоторую путаницу.
Функция WndProc возвращает значение типа LRESULT. Оно определено просто как LONG. Функция WinMain получает тип WINAPI (как и любая другая функция Windows, которая определяется в заголовочных файлах), а функция WndProc получает тип CALLBACK. Оба эти идентификатора определяются как stdcall, что является ссылкой на особую последовательность вызовов функций, которая имеет место между самой операционной системой Windows и ее приложением.
В HELLOWIN также использованы четыре структуры данных (о которых будет рассказано в конце этой главы), определяемых в заголовочных файлах Windows. Этими структурами данных являются:
Структура
Значение
MSG
Структура сообщения
WNDCLASSEX
Структура класса окна
PAINTSTRUCT
Структура рисования
RECT
Структура прямоугольника
Первые две структуры данных используются в WinMain для определения двух структур, названных msg и wndclass.
Две вторые используются в WndProc для определения структур ps и rect.
Описатели
Наконец, имеется еще три идентификатора, которые пишутся прописными буквами и предназначены для разных типов описателей (handles):
Идентификатор
Значение
HINSTANCE
Описатель экземпляра (instance) самой программы
HWND
Описатель окна
HDC
Описатель контекста устройства

28
Описатели в Windows используются довольно часто. Перед тем как эта глава подойдет к концу, вы встретите описатель HICON (описатель иконки), описатель HCURSOR (описатель курсора мыши) и описатель HBRUSH
(описатель графической кисти).
Описатель — это просто число (обычно длиной в 32 разряда), которое ссылается на объект. Описатели в Windows напоминают описатели файлов при программировании на традиционном С в MS-DOS. Программа почти всегда получает описатель путем вызова функции Windows. Программа использует описатель в других функциях
Windows, чтобы сослаться на объект. Действительное значение описателя весьма важно для вашей программы, но модуль Windows, который обеспечивает программу описателем, "знает", как его использовать для ссылки на объект.
Венгерская нотация
Как вы могли заметить, некоторые переменные в HELLOWIN.С имеют своеобразные имена. Например, имя
szCmdLine — параметр WinMain.
Многие программисты для Windows используют соглашения по именованию переменных, названные условно
Венгерской нотацией, в честь легендарного программиста Microsoft Чарльза Симони. Все очень просто: имя переменной начинается со строчных буквы или букв, которые отмечают тип данных переменной. Например, префикс sz в szCmdLine означает, что строка завершается нулем (string terminated by zero). Префикс h в hInstance и
hPrevInstance означает описатель (handle); префикс i в iCmdShow означает целое (integer). В двух последних параметрах WndProc также используется венгерская нотация, хотя, как уже говорилось раньше, wParam правильнее следовало бы назвать uiParam (беззнаковое целое — unsigned integer). Но поскольку эти два параметра определяются через типы данных WPARAM и LPARAM, было решено сохранить их прежние имена.
При обозначении переменных структуры удобно пользоваться именем самой структуры (или аббревиатурой имени структуры) и строчными буквами, используя их либо в качестве префикса имени переменной, либо как имя переменной в целом. Например, в функции WinMain в HELLOWIN.С переменная msg относится к структуре типа
MSG; wndclass — к структуре типа WNDCLASSEX. В функции WndProc, переменная ps относится к структуре
PAINTSTRUCT, rect — к RECT.
Венгерская нотация помогает избегать ошибок в программе еще до ее компоновки. Поскольку имя переменной описывает и саму переменную и тип ее данных, то намного снижается вероятность введения в программу ошибок, связанных с несовпадением типа данных у переменных.
В следующей таблице представлены префиксы переменных, которые в основном будут использоваться в этой книге:
Префикс
Тип данных
c
символ
by
BYTE (беззнаковый символ)
n
короткое целое
i
целое
x, y
целое (используется в качестве координат x и y)
cx, cy
целое (используется в качестве длины x и y), с означает "счет" — (count)
b или f
BOOL (булево целое); f означает "флаг" — (flag)
w
WORD (беззнаковое короткое целое)
l
LONG (длинное целое)
dw
DWORD (беззнаковое длинное целое)
fn
функция
s
строка
sz
строка, завершаемая нулем
h
описатель (handle)
p указатель (pointer)
Точка входа программы
На этом глобальный обзор HELLOWIN.С заканчивается, и можно начать строку за строкой разбирать программу.
Текст программы начинается с инструкции #include, которая позволяет включить в программу заголовочный файл
WINDOWS.H:
#include
WINDOWS.H включает в себя много других заголовочных файлов, содержащих объявления функций Windows, структур Windows, новые типы данных и числовые константы.
За инструкцией #include следует объявление WndProc:
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

29
Это объявление в начале программы необходимо потому, что в тексте функции WinMain имеются ссылки на функцию WndProc.
В программе на языке С, написанной для традиционной среды, точкой входа является функция main. С этого места программа начинает выполняться. (Фактически функция main является точкой входа в ту часть программы, которая пишется программистом. Обычно компилятор С должен вставить некоторый стартовый код в исполняемый файл. Этот код и вызывает функцию main.) Точкой входа программы для Windows является функция
WinMain. WinMain всегда определяется следующим образом: int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
Эта функция использует последовательность вызовов WINAPI и, по своему завершению, возвращает операционной системе Windows целое. Функция называется WinMain. В ней есть четыре параметра.
Параметр hInstance называется описателем экземпляра (instance handle). Это уникальное число, идентифицирующее программу, когда она работает под Windows. Может так случиться, что пользователь запустит под Windows несколько копий одной и той же программы. Каждая копия называется "экземпляром" и у каждой свое значение hInstance. Описатель экземпляра можно сравнить с "идентификатором задачи" или "идентификатором процесса" — обычными терминами многозадачных операционных систем.
Параметр hPrevInstance — предыдущий экземпляр (previous instance) — в настоящее время устарел. В ранних версиях Windows он относился к самому последнему, предшествующему данному, описателю экземпляра той программы, которая все еще активна. Если в данный момент времени не было загружено никаких копий программы, то hPrevInstance = 0 или NULL. Под Windows 95 этот параметр всегда равен NULL.
Параметр szCmdLine — это указатель на оканчивающуюся нулем строку, в которой содержатся любые параметры, переданные в программу из командной строки. Вы можете запустить программу для Windows с параметром командной строки, вставив этот параметр после имени программы в командной строке MS-DOS или указать имя программы и параметр в окне диалога Run, которое вызывается из меню Start.
Параметр iCmdShow — число, показывающее, каким должно быть выведено на экран окно в начальный момент.
Это число задается при запуске программы другой программой. Программисты достаточно редко обращаются к этому числу, но при необходимости такая возможность существует. В большинстве случаев число равно 1 или 7.
Но лучше не думать об этом значении как о единице или как о семерке. Лучше думайте о них как об идентификаторе SW_SHOWNORMAL (заданном в заголовочных файлах Windows равным 1) или идентификаторе
SW_SHOWMINNOACTIVE (заданном равным 7). Префикс SW в этих идентификаторах означает "показать окно"
(show window). Параметр показывает, необходимо ли запущенную пользователем программу выводить на экран в виде окна нормального размера или окно должно быть изначально свернутым.
Регистрация класса окна
Окно всегда создается на основе класса окна. Класс окна идентифицирует оконную процедуру, которая выполняет процесс обработки сообщений, поступающих окну. Поскольку это важно, повторяем: окно всегда создается на основе класса окна. Класс окна идентифицирует оконную процедуру, которая выполняет процесс обработки сообщений, поступающих окну.
На основе одного класса окна можно создать несколько окон. Например, все окна-кнопки в Windows создаются на основе одного и того же класса окна. Класс окна определяет оконную процедуру и некоторые другие характеристики окон, создаваемых на основе этого класса. Когда вы создаете окно, вы определяете дополнительные характеристики окна, уникальные для него.
Перед созданием окна для вашей программы необходимо зарегистрировать класс окна путем вызова функции
RegisterClassEx. Это расширенная (на что указывает окончание названия Ex, т. е. extended — расширенный) версия функции RegisterClass из предыдущих версий Windows. Тем не менее функция RegisterClass продолжает работать и под Windows 95.
У функции RegisterClassEx имеется один параметр: указатель на структуру типа WNDCLASSEX. Структура
WNDCLASSEX определяется в заголовочных файла Windows следующим образом: typedef struct tagWNDCLASSEX
{
UINT cbSize;
UINT style;
WNDPROC lpfnWndProc; int cbClsExtra; int cbWndExtra;
HINSTANCE hInstance;
HICON hIcon;
HCURSOR hCursor;

30
HBRUSH hbrBackground;
LPCSTR lpszMenuName;
LPCSTR lpszClassName;
HICON hIconSm;
} WNDCLASSEX;
Несколько замечаний о некоторых из представленных здесь типах данных и о венгерской нотации: префиксы LP и
lp означают "длинный указатель" (long pointer), являющийся пережитком 16-разрядной Windows, в которой программисты могли различать короткие (или близкие, near) 16-разрядные указатели и длинные (или дальние, far)
32-разрядные указатели. В Windows 95 все указатели имеют длину в 32 разряда. В представленных в этой книге программах все префиксы l для типов указателей убраны, но несомненно вы встретите их где-нибудь в другом месте.
Обратите также внимание на некоторые новые случаи использования венгерской нотации: приставка lpfn означает "длинный указатель на функцию" (long pointer to a function). Приставка cb означает "счетчик байтов" (counter of bytes). Префикс hbr — это "описатель кисти" (handle to a brush).
В WinMain вы должны определить структуру типа WNDCLASSEX, обычно это делается следующим образом:
WNDCLASSEX wndclass;
Затем задаются 12 полей структуры и вызывается RegisterClassEx:
RegisterClassEx(&wndclass);
Наиболее важными являются второе от конца и третье поля. Второе от конца поле является именем класса окна
(который в программах, создающих одно окно, обычно совпадает с именем программы). Третье поле (lpfnWndProc) является адресом оконной процедуры, которая используется для всех окон, созданных на основе этого класса (в
HELLOWIN.C оконной процедурой является функция WndProc). Другие поля описывают характеристики всех окон, создаваемых на основе этого класса окна.
Поле cbSize равно длине структуры. Инструкция: wndclass.style = CS_HREDRAW | CS_VREDRAW; осуществляет объединение двух идентификаторов "стиля класса" (class style) с помощью поразрядной операции
OR языка С. В заголовочных файлах Windows, идентификаторы, начинающиеся с префикса CS, задаются в виде
32-разрядной константы, только один из разрядов которой установлен в 1. Например, CS_VREDRAW задан как
0x0001, а CS_HREDRAW как 0x0002. Заданные таким образом идентификаторы иногда называют "поразрядными флагами" (bit flags). Объединяются поразрядные флаги с помощью операции OR языка С.
Эти два идентификатора стиля класса показывают, что все окна, созданные на основе данного класса должны целиком перерисовываться при изменении горизонтального (CS_HREDRAW) или вертикального (CS_VREDRAW) размеров окна. Если вы измените размер окна HELLOWIN, то увидите, что строка текста переместится в новый центр окна. Эти два идентификатора гарантируют, что это случится. Далее мы подробно рассмотрим, как оконная процедура уведомляется об изменении размера окна.
Третье поле структуры WNDCLASSEX инициализируется с помощью инструкции: wndclass.lpfnWndProc = WndProc;
Эта инструкция устанавливает оконную WndProc как оконную процедуру данного окна, которая является второй функцией в HELLOWIN.С. Эта оконная процедура будет обрабатывать все сообщения всем окнам, созданным на основе данного класса окна. Как уже упоминалось, приставка lpfn означает "длинный указатель на функцию".
Следующие две инструкции: wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; резервируют некоторое дополнительное пространство в структуре класса и структуре окна, которое внутренне поддерживается операционной системой Windows. Программа может использовать это свободное пространство для своих нужд. В HELLOWIN эта возможность не используется, поэтому соответствующие значения равны 0. В противном случае, как следует из венгерской нотации, в этом поле было бы установлено "число байтов" резервируемой памяти.
В следующем поле находится просто описатель экземпляра программы (который является одним из параметров
WinMain): wndclass.hInstance = hInstance;
Инструкции: wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);

31 и wndclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION); устанавливают значок для всех окон, созданных на основе данного класса окна. Значок — это просто маленькая битовая картинка, которая появляется на панели задач Windows и слева в заголовке окна. Позже из этой книги вы узнаете, как создавать пользовательские значки для ваших программ под Windows. Сейчас для простоты воспользуемся стандартным значком.
Для получения описателя стандартного значка, вы вызываете LoadIcon, установив первый параметр в NULL. (При загрузке вашего собственного пользовательского значка, этот параметр должен быть установлен равным описателю экземпляра программы.) Второй идентификатор, начинающийся с префикса IDI ("идентификатор для значка" — ID for icon) определяется в заголовочных файлах Windows. Значок IDI_APPLICATION — это просто маленькое изображение окна. Функция LoadIcon возвращает описатель этого значка. Фактически нам не важно конкретное значение этого описателя. Оно просто используется для установки значений полей wndclass.hIcon и
wndclass.hIconSm. Эти поля определяются в структуре WNDCLASSEX как поля типа HICON, что означает "описатель значка" (handle to an icon).
Инструкция: wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); очень похожа на две предыдущие инструкции. Функция LoadCursor загружает стандартный курсор IDC_ARROW и возвращает описатель курсора. Этот описатель присваивается полю hCursor структуры WNDCLASSEX. Когда курсор мыши оказывается в рабочей области окна, созданного на основе данного класса, он превращается в маленькую стрелку.
Следующее поле задает цвет фона рабочей области окон, созданных на основе данного класса. Префикс hbr имени поля hbrBackground означает "описатель кисти" (handle to a brush). Кисть — это графический объект, который представляет собой шаблон пикселей различных цветов, используемый для закрашивания области. В Windows имеется несколько стандартных, или предопределенных (stock) кистей. Вызов GetStockObject, показанный здесь, возвращает описатель белой кисти: wndclass.hbrBackground = GetStockObject(WHITE_BRUSH);
Это означает, что фон рабочей области окна будет плотного белого цвета, что является стандартным выбором.
Следующее поле задает меню класса окна. В приложении HELLOWIN меню отсутствует, поэтому поле установлено в NULL: wndclass.lpszMenuName = NULL;
На последнем этапе классу должно быть присвоено имя. Для простой программы оно может быть просто именем программы, которым в нашем случае является строка "HelloWin", хранящаяся в переменной szAppName: wndclass.lpszClassName = szAppName;
После того как инициализированы все 12 полей структуры, HELLOWIN регистрирует класс окна путем вызова функции RegisterClassEx. Единственным параметром функции является указатель на структуру WNDCLASSEX:
RegisterClassEx(&wndclass);
Создание окна
Класс окна определяет основные характеристики окна, что позволяет использовать один и тот же класс для создания множества различных окон. Когда вы, вызывая функцию CreateWindow, фактически создаете окно, вы, таким образом, детализируете информацию об окне.
Новички в программировании для Windows иногда путают понятия класс окна и окно, и не понимают, почему нельзя все характеристики окна задать за один раз. Фактически же, делить таким образом информацию очень удобно. Например, все окна-кнопки создаются на основе одного и того же класса окна. Оконная процедура, связанная с этим классом окна, находится в самой операционной системе Windows. Класс окна определяет для этих кнопок процесс ввода информации с клавиатуры, а также с помощью мыши, и одновременно регламентирует отображение кнопок на экране. Все кнопки, таким образом, работают одинаково. Но, в то же время, сами кнопки не одинаковы. Они могут отличаться по размеру, располагаться в разных местах экрана, иметь отличные друг от друга надписи. Эти последние характеристики и задаются при определении окна, а не класса окна.
Вместо использования структуры данных, как это делается в случае использования функции RegisterClassEx, вызов функции CreateWindow требует, чтобы вся информация передавалась функции в качестве параметров. Далее представлен вызов функции CreateWindow в HELLOWIN.C:

32 hwnd = CreateWindow( szAppName,
//
имя класса окна "The Hello Program",
//
заголовок окна
WS_OVERLAPPEDWINDOW,
//
стиль окна
CW_USEDEFAULT,
//начальное положение по x
CW_USEDEFAULT,
//начальное положение по y
CW_USEDEFAULT,
//начальный размер по x
CW_USEDEFAULT,
//начальный размер по y
NULL,
//описатель родительского окна
NULL,
//описатель меню окна hInstance,
//
описатель экземпляра программы
NULL
);
//параметры создания
Для удобства восприятия, использовались символ // и однострочные комментарии для описания параметров функции CreateWindow.
Параметр с комментарием "имя класса окна" — szAppName содержит строку "HelloWin", являющуюся именем только что зарегистрированного класса окна. Таким образом, этот параметр связывает окно с классом окна.
Окно, созданное нашей программой, является обычным перекрывающимся окном с заголовком, системным меню слева на строке заголовка, иконками для сворачивания, разворачивания и закрытия окна справа на строке заголовка и рамкой окна. Это стандартный стиль окон, он называется WS_OVERLAPPEDWINDOW и помечен комментарием "стиль окна". Комментарием "заголовок окна" отмечен текст, который появится в строке заголовка.
Параметры с комментариями "начальное положение по x" и "начальное положение по y" задают начальные координаты верхнего левого угла окна относительно левого верхнего угла экрана. Устанавливая для этих параметров идентификатор CW_USEDEFAULT, мы сообщаем Windows, что хотим использовать для перекрывающегося окна задаваемое по умолчанию начальное положение. (CW_USEDEFAULT задается равным
0x80000000.) По умолчанию Windows располагает следующие друг за другом перекрывающиеся окна, равномерно отступая по горизонтали и вертикали от верхнего левого угла экрана. Примерно также задают ширину и высоту окна параметры с комментариями "начальный размер по x" и "начальный размер по y". CW_USEDEFAULT снова означает, что мы хотим, чтобы Windows использовала задаваемый по умолчанию размер окна.
Параметр с комментарием "описатель родительского окна" устанавливается в NULL, поскольку у нашего окна отсутствует родительское окно. (Если между двумя окнами существует связь типа родительское-дочернее, дочернее окно всегда появляется только на поверхности родительского.) Параметр с комментарием "описатель меню окна" также установлен в NULL, поскольку у нашего окна нет меню. В параметр с комментарием "описатель экземпляра программы" помещается описатель экземпляра, переданный программе в качестве параметра функции
WinMain. И наконец, параметр с комментарием "параметры создания" установлен в NULL. При необходимости этот параметр используется в качестве указателя на какие-нибудь данные, на которые программа в дальнейшем могла бы ссылаться.
Вызов CreateWindow возвращает описатель созданного окна. Этот описатель хранится в переменной hwnd, которая имеет тип HWND (описатель окна — handle to a window). У каждого окна в Windows имеется описатель. В вашей программе описатель используется для того, чтобы ссылаться на окно. Для многих функций Windows в качестве параметра требуется hwnd, благодаря этому Windows знает, к какому окну применить функцию. Если программа создает несколько окон, то каждое из них имеет свой описатель. Описатель окна — это один из важнейших описателей, которыми оперирует программа для Windows.
Отображение окна
К тому времени, когда функция CreateWindow возвращает управление программе, окно уже создано внутри
Windows. Однако, на экране монитора оно еще не появилось. Необходимы еще два вызова. Первый из них:
ShowWindow(hwnd, iCmdShow);
Первым параметром является описатель только что созданного функцией CreateWindow окна. Вторым параметром является величина iCmdShow, передаваемая в качестве параметра функции WinMain. Он задает начальный вид окна на экране. Если iCmdShow имеет значение SW_SHOWNORMAL (т. е. 1), на экран выводится обычное окно. Если
iCmdShow имеет значение SW_SHOWMINNOACTIVE (т. е. 7), то окно не выводится, а на панели задач появляются его имя и иконка.
Функция ShowWindow выводит окно на экран. Если второй параметр ShowWindow имеет значение
SW_SHOWNORMAL, то фон рабочей области окна закрашивается той кистью, которая задана в классе окна. Вызов функции:
UpdateWindow(hwnd);

33 вызывает затем перерисовку рабочей области. Для этого в оконную процедуру (функция WndProc в HELLOWIN.C) посылается сообщение WM_PAINT. Вскоре мы изучим, как WndProc обрабатывает это сообщение.
Цикл обработки сообщений
После вызова функции UpdateWindow, окно окончательно выведено на экран. Теперь программа должна подготовить себя для получения информации от пользователя через клавиатуру и мышь. Windows поддерживает "очередь сообщений" (message queue) для каждой программы, работающей в данный момент в системе Windows.
Когда происходит ввод информации, Windows преобразует ее в "сообщение", которое помещается в очередь сообщений программы.
Программа извлекает сообщения из очереди сообщений, выполняя блок команд, известный как "цикл обработки сообщений" (message loop): while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
} return msg.wParam;
Переменная msg — это структура типа MSG, которая определяется в заголовочных файлах Windows следующим образом: typedef struct tagMSG
{
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
} MSG;
Тип данных POINT — это тип данных другой структуры, которая определяется так: typedef struct tagPOINT
{
LONG x;
LONG y;
} POINT;
Вызов функции GetMessage, с которого начинается цикл обработки сообщений, извлекает сообщение из очереди сообщений:
GetMessage(&msg, NULL, 0, 0)
Этот вызов передает Windows указатель на структуру msg типа MSG. Второй, третий и четвертый параметры,
NULL или 0, показывают, что программа получает все сообщения от всех окон, созданных этой программой.
Windows заполняет поля структуры сообщений информацией об очередном сообщении из очереди сообщений.
Поля этой структуры следующие:

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

message — идентификатор сообщения. Это число, которое идентифицирует сообщение. Для каждого сообщения имеется соответствующий ему идентификатор, который задается в заголовочных файлах
Windows и начинается с префикса WM (оконное сообщение — window message). Например, если вы установите указатель мыши в рабочей области программы HELLOWIN и нажмете левую кнопку мыши,
Windows поставит сообщение в очередь сообщений с полем message равным WM_LBUTTONDOWN, значение которого 0x0201.

wParam — 32-разрядный параметр сообщения (message parameter), смысл и значение которого зависят от особенностей сообщения.

lParam — другой 32-разрядный параметр, зависящий от сообщения.

time — время, когда сообщение было помещено в очередь сообщений.

pt — координаты курсора мыши в момент помещения сообщения в очередь сообщений.

34
Если поле message сообщения, извлеченного из очереди сообщений, равно любому значению, кроме WM_QUIT
(т. е., 0x0012), то функция GetMessage возвращает ненулевое значение. Сообщение WM_QUIT заставляет программу прервать цикл обработки сообщений. На этом программа заканчивается, возвращая число wParam структуры msg.
Инструкция:
TranslateMessage(&msg); передает структуру msg обратно в Windows для преобразования какого-либо сообщения с клавиатуры. (Более подробно об этом будет рассказано в главе 5.)
Инструкция:
DispatchMessage(&msg); также передает структуру msg обратно в Windows. Windows отправляет сообщение для его обработки соответствующей оконной процедуре — таким образом, Windows вызывает оконную процедуру. Такой оконной процедурой в HELLOWIN является функция WndProc. После того, как WndProc обработает сообщение, оно возвращается в Windows, которая все еще обслуживает вызов функции DispatchMessage. Когда Windows возвращает управление в программу HELLOWIN к следующему за вызовом DispatchMessage коду, цикл обработки сообщений в очередной раз возобновляет работу, вызывая GetMessage.
Оконная процедура
Обо всем, о чем так долго говорилось, можно кратко сказать так: класс окна зарегистрирован, окно создано, окно выведено на экран, и для извлечения сообщений из очереди сообщений программа вошла в цикл обработки сообщений.
Реальная работа начинается в оконной процедуре, которую программисты обычно называют "window proc".
Оконная процедура определяет то, что выводится в рабочую область окна и то, как окну реагировать на пользовательский ввод.
В программе HELLOWIN оконной процедурой является функция WndProc. Оконной процедуре можно назначить любое имя (любое, конечно, в той степени, в которой оно не будет конфликтовать с другими именами). В программе для Windows может содержаться более одной оконной процедуры. Оконная процедура всегда связана с определенным классом окна, который вы регистрируете, вызывая RegisterClassEx. Функция CreateWindow создает окно на основе определенного класса окна. На основе одного и того же класса можно создать несколько окон.
Оконная процедура всегда определяется следующим образом:
LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
Отметьте, что четыре параметра оконной процедуры идентичны первым четырем полям структуры MSG.
Первым параметром является hwnd, описатель получающего сообщение окна. Это тот же описатель, который возвращает функция CreateWindow. Для программы типа HELLOWIN, в которой создается только одно окно, имеется только один известный программе описатель окна. Если же в программе создается несколько окон на основе одного и того же класса окна (и следовательно одной и той же оконной процедуры), тогда hwnd идентифицирует конкретное окно, которое получает сообщение.
Вторым параметром является число (точнее 32-разрядное беззнаковое целое или UINT), которое идентифицирует сообщение. Два последних параметра (wParam типа WPARAM и lParam LPARAM) представляют дополнительную информацию о сообщении. Они называются "параметрами сообщения" (message parameters). Конкретное значение этих параметров определяется типом сообщения.
Обработка сообщений
Каждое получаемое окном сообщение идентифицируется номером, который содержится в параметре iMsg оконной процедуры. В заголовочных файлах Windows определены идентификаторы, начинающиеся с префикса WM
("window message") для каждого типа сообщений.
Обычно программисты для Windows используют конструкции switch и case для определения того, какое сообщение получила оконная процедура и то, как его обрабатывать. Если оконная процедура обрабатывает сообщение, то ее возвращаемым значением должен быть 0. Все сообщения, не обрабатываемые оконной процедурой, должны передаваться функции Windows, которая называется DefWindowProc. Значение, возвращаемое функцией DefWindowProc, должно быть возвращаемым значением оконной процедуры.
В HELLOWIN функция WndProc обрабатывает только три сообщения: WM_CREATE, WM_PAINT и
WM_DESTROY. Оконная процедура выглядит следующим образом:

35 switch(iMsg)
{ case WM_CREATE:
[process WM_CREATE message]
return
0; case WM_PAINT:
[process WM_PAINT message]
return
0; case WM_DESTROY:
[process WM_DESTROY message]
return
0;
} return DefWindowProc(hwnd, iMsg, wParam, lParam);
Здесь важно отметить то, что вызов функции DefWindowProc обрабатывает по умолчанию все сообщения, которые не обрабатывает ваша оконная процедура.
Воспроизведение звукового файла
Самое первое сообщение, которое получает оконная процедура — и первое, которое обрабатывает функция
WndProc — это WM_CREATE. WndProc получает это сообщение тогда, когда Windows обрабатывает функцию
CreateWindow в WinMain. Таким образом, когда HELLOWIN вызывает CreateWindow, Windows делает то, что должна делать, т. е. Windows вызывает WndProc с описателем окна в качестве первого параметра и с
WM_CREATE в качестве второго. WndProc обрабатывает сообщение WM_CREATE и передает управление обратно в Windows. Теперь Windows может вернуться после вызова CreateWindow обратно в HELLOWIN, чтобы продолжить работу в WinMain.
Часто оконная процедура выполняет разовую инициализацию окна, когда обрабатывается сообщение.
WM_CREATE. HELLOWIN предпочитает обрабатывать это сообщение путем воспроизведения звукового файла
HELLOWIN.WAV. Это делается с помощью функции PlaySound. Первым параметром этой функции является имя файла. Это также может быть другое имя (sound alias name), которое задается в секции Sounds панели управления
(Control Panel) или определяется ресурсом программы. Второй параметр используется только при условии, что звуковой файл является ресурсом. Третий параметр задает две опции. В нашем случае, когда первый параметр — это имя файла, звук должен воспроизводиться асинхронно, т. е. функция PlaySound возвратит свое значение как только начнет воспроизводиться звуковой файл, не ожидая окончания воспроизведения.
WndProc завершает обработку WM_CREATE с нулевым возвращаемым значением.
Сообщение WM_PAINT
Сообщение WM_PAINT функция WndProc обрабатывает вторым. Это сообщение крайне важно для программирования под Windows. Оно сообщает программе, что часть или вся рабочая область окна недействительна (invalid), и ее следует перерисовать.
Как рабочая область становится недействительной? При первом создании окна недействительна вся рабочая зона, поскольку программа еще ничего в окне не нарисовала. Сообщение WM_PAINT (которое обычно посылается, когда программа вызывает UpdateWindow в WinMain) заставляет оконную процедуру что-то нарисовать в рабочей области.
Когда вы изменяете размер окна, рабочая область также становится недействительной. Вспомните, что в параметр
style структуры wndclass программы HELLOWIN помещены флаги CS_HREDRAW и CS_VREDRAW. Они заставляют Windows при изменении размеров окна считать недействительным все окно. Затем оконная процедура получает сообщение WM_PAINT.
Когда вы минимизируете окно программы HELLOWIN, а затем снова его восстанавливаете до начального размера, то в Windows содержимое рабочей области не сохраняется. В графической среде это привело бы к тому, что пришлось бы хранить слишком много данных. Вместо этого, Windows делает недействительным все окно. Оконная процедура получает сообщение WM_PAINT и сама восстанавливает содержимое окна.
Когда вы перемещаете окна так, что они перекрываются, Windows не сохраняет ту часть окна, которая закрывается другим окном. Когда эта часть окна позже открывается, Windows помечает его как недействительное. Оконная процедура получает сообщение WM_PAINT для восстановления содержимого окна.
Обработка сообщения WM_PAINT почти всегда начинается с вызова функции BeginPaint: hdc = BeginPaint(hwnd, &ps);

36 и заканчивается вызовом функции EndPaint:
EndPaint(hwnd, &ps);
В обеих функциях первый параметр — это описатель окна программы, а второй — это указатель на структуру типа
PAINTSTRUCT. В структуре PAINTSTRUCT содержится некоторая информация, которую оконная процедура может использовать для рисования в рабочей области. (В следующей главе будет рассказано о полях этой структуры.)
При обработке вызова BeginPaint, Windows обновляет фон рабочей области, если он еще не обновлен. Обновление фона осуществляется с помощью кисти, заданной в поле hbrBackground структуры WNDCLASSEX, которая использовалась при регистрации класса окна. В случае нашей программы HELLOWIN подготовлена белая кисть и это означает, что Windows обновит фон окна, закрасив его белым цветом. Вызов BeginPaint делает всю рабочую область действительной (не требующей перерисовки) и возвращает описатель контекста устройства. Контекст устройства описывает физическое устройство вывода информации (например, дисплей) и его драйвер. Описатель контекста устройства необходим вам для вывода в рабочую область окна текста и графики. Используя описатель контекста устройства, возвращаемого функцией BeginPaint, вы не сможете рисовать вне рабочей области, даже не пытайтесь. Функция EndPaint освобождает описатель контекста устройства, после чего его значение нельзя использовать.
Если оконная процедура не обрабатывает сообщения WM_PAINT (что бывает крайне редко), они должны передаваться в DefWindowProc. Функция DefWindowProc просто по очереди вызывает BeginPaint и EndPaint и, таким образом, рабочая область устанавливается в действительное состояние, т. е. состояние, не требующее перерисовки.
После того, как WndProc вызвала BeginPaint, она вызывает GetClientRect:
GetClientRect(hwnd, &rect);
Первый параметр — это описатель окна программы. Второй параметр — это указатель на переменную rect, для которой в WndProc задан тип RECT.
RECT — это структура "прямоугольник" (rectangle), определенная в заголовочных файлах Windows. Она имеет четыре поля типа LONG, имена полей: left, top, right и bottom. GetClientRect помещает в эти четыре поля размер рабочей области окна. Поля left и top всегда устанавливаются в 0. В полях right и bottom устанавливается ширина и высота рабочей области в пикселях.
WndProc никак не использует структуру RECT, за исключением передачи указателя на нее в качестве четвертого параметра функции DrawText:
DrawText(hdc, "Hello, Windows 95!", -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
DrawText (как подсказывает ее имя) рисует текст. Поскольку эта функция что-то рисует, то первый параметр — это описатель контекста устройства, возвращенный функцией BeginPaint. Вторым параметром является рисуемый текст, а третий параметр установлен в —1, чтобы показать, что строка текста заканчивается нулевым символом.
Последний параметр — это набор флагов, значения которых задано в заголовочных файлах Windows. Флаги показывают, что текст следует выводить в одну строку, по центру относительно горизонтали и вертикали и внутри прямоугольной области, размер которой задан четвертым параметром. Вызов этой функции приводит, таким образом, к появлению строки "Hello, Windows 95!" в центре рабочей области.
Когда рабочая область становится недействительной (как это происходит при изменении размеров окна), WndProc получает новое сообщение WM_PAINT. Новый размер окна WndProc получает, вызвав функцию GetClientRect, и снова рисует текст в центре окна.
Сообщение WM_DESTROY
Еще одним важным сообщением является сообщение WM_DESTROY. Это сообщение показывает, что Windows находится в процессе ликвидации окна в ответ на полученную от пользователя команду. Пользователь вызывает поступление этого сообщения, если щелкнет на кнопке Close, или выберет Close из системного меню программы, или нажмет +.
HELLOWIN стандартно реагирует на это сообщение, вызывая:
PostQuitMessage(0);
Эта функция ставит сообщение WM_QUIT в очередь сообщений программы. Как уже упоминалось, функция
GetMessage возвращает ненулевое значение при любом сообщении, полученном из очереди сообщений за исключением WM_QUIT. Когда GetMessage получает сообщение WM_QUIT, функция возвращает 0. Это заставляет WinMain прервать цикл обработки сообщений и выйти в систему, закончив программу.

37
Сложности программирования для Windows
Даже с учетом авторских пояснений структура и принципы работы программы Hellowin, вполне вероятно, так и останутся для вас немного загадочными. В короткой программе на С, написанной для обычной среды, вся программа целиком может поместиться в функции main. В HELLOWIN WinMain содержит только надстройку программы, необходимую для регистрации класса окна, создания окна и получения и передачи сообщений из/в очередь сообщений.
Все основные действия программы происходят в оконной процедуре. В HELLOWIN этих действий немного — это просто воспроизведение звука и вывод в окно строки текста. Но в следующих главах вы обнаружите, что почти в каждой программе для Windows работа с сообщениями происходит в оконной процедуре. Это основная концептуальная особенность, которую вы должны усвоить, чтобы начать писать программы для Windows.
Не вызывай меня, я вызову тебя
Как уже упоминалось, программисты хорошо знакомы с понятием вызова операционной системы для выполнения каких-то действий. Например, программисты на С используют функцию fopen для открытия файла. Библиотечные функции, поставляемые с компилятором, содержат код, который фактически вызывает для открытия файла операционную систему. Здесь все просто.
Но операционная система Windows ведет себя иначе. Хотя в Windows имеется свыше тысячи доступных программисту функций, Windows также и сама посылает вызовы вашей программе, особенно оконной процедуре, которую мы назвали WndProc. Оконная процедура связана с классом окна, который программа регистрирует с помощью вызова функции RegisterClassEx. Окно, создаваемое на основе этого класса, использует оконную процедуру для обработки всех сообщений окна. Windows посылает сообщения окну, вызывая оконную процедуру.
Windows вызывает WndProc первый раз при создании окна. Windows вызывает WndProc при последующем удалении окна. Windows вызывает WndProc при изменении размеров окна, при его перемещении, при его свертывании. Windows вызывает WndProc при выборе пункта меню. Windows вызывает WndProc при манипуляциях с полосами прокрутки или с мышью. Windows вызывает WndProc, чтобы сообщить ей о необходимости перерисовать рабочую область.
Все эти вызовы имеют форму сообщений. В большинстве программ для Windows, основная часть программы направлена на обработку этих сообщений. Свыше 200 различных сообщений, которые Windows может отправить оконной процедуре, идентифицируются именами, которые начинаются с букв "WM" и определяются в заголовочных файлах Windows.
Фактически, идея функции, находящейся в программе, но которая вызывается не из самой программы, не является абсолютно новой в традиционном программировании. Функция signal в С может перехватить +. Вы можете иметь опыт с перехватом аппаратных прерываний с помощью языка ассемблера или одной из конструкций
ON в Microsoft BASIC. Драйвер мыши фирмы Microsoft позволяет работать с этой мышью программам, сделанным не для Windows.
В Windows эта идея расширена и пронизывает всю систему. Любое событие, относящееся к окну, передается оконной процедуре в виде сообщения. Затем оконная процедура соответствующим образом реагирует на это сообщение или передает сообщение в DefWindowProc для обработки его по умолчанию.
Параметры wParam и lParam оконной процедуры не используются в HELLOWIN кроме как параметры для
DefWindowProc. Эти параметры дают оконной процедуре дополнительную информацию о сообщении. Значение этих параметров зависит от самого сообщения.
Давайте рассмотрим пример. Когда меняется размер рабочей области окна, Windows вызывает оконную процедуру.
Параметр hwnd оконной процедуры — это описатель окна, изменившего размер. Параметр iMsg равен WM_SIZE.
Параметр wParam для сообщения WM_SIZE равен одной из величин SIZENORMAL, SIZEICONIC,
SIZEFULLSCREEN, SIZEZOOMSHOW или SIZEZOOMHIDE (определяемых в заголовочных файлах Windows как числа от 0 до 4). Параметр wParam показывает, будет ли окно свернуто, развернуто или скрыто (в результате развертывания другого окна). Параметр lParam определяет новый размер окна. Новая ширина (16-разрядное значение) и новая высота (16-разрядное значение) объединяются вместе в 32-разрядный параметр lParam. В заголовочных файлах Windows имеется макрос, который позволяет выделить оба эти значения из lParam. Мы это проделаем в следующей главе.
Иногда, в результате обработки сообщения функцией DefWindowProc, генерируются другие сообщения. Например, предположим, что вы запускаете HELLOWIN и выбираете Close из системного меню программы, используя клавиатуру или мышь. DefWindowProc обрабатывает эту информацию. Когда она определяет, что вы выбрали опцию Close, то отправляет сообщение WM_SYSCOMMAND оконной процедуре. WndProc передает это сообщение DefWindowProc. DefWindowProc реагирует на него, отправляя сообщение WM_CLOSE оконной процедуре. WndProc снова передает это сообщение DefWindowProc. DefWindowProc реагирует на сообщение

38
WM_CLOSE, вызывая функцию DestroyWindow. DestroyWindow заставляет Windows отправить сообщение
WM_DESTROY оконной процедуре. И наконец, WndProc реагирует на это сообщение, вызывая функцию
PostQuitMessage путем постановки сообщения WM_QUIT в очередь сообщений. Это сообщение прерывает цикл обработки сообщений в WinMain и программа заканчивается.
Синхронные и асинхронные сообщения
Ранее было рассказано о передаче окну сообщений, что означает вызов операционной системой Windows оконной процедуры. Но в программах для Windows имеется цикл обработки сообщений, который берет сообщения из очереди сообщений, вызывая функцию GetMessage, и отправляет их оконной процедуре, вызывая функцию
DispatchMessage.
Так что же, буферизуются ли сообщения для Windows-программы (так же как в обычной программе буферизуется ввод с клавиатуры) и затем пересылаются дальше, или она (программа для Windows) получает сообщения непосредственно снаружи? И так, и этак.
Одни и те же сообщения могут быть и "синхронные" (queued), и "асинхронные" (nonqueued)
1
. Синхронными сообщениями называются сообщения, которые Windows помещает в очередь сообщений программы, и которые извлекаются и диспетчеризуются в цикле обработки сообщений. Асинхронные сообщения передаются непосредственно окну, когда Windows вызывает оконную процедуру. В результате оконная процедура получает все предназначенные для окна сообщения, как синхронные, так и асинхронные. Структура программ для Windows очень проста, поскольку у них имеется только одно центральное место обработки сообщений. Говорят, что синхронные сообщения помещаются в очередь сообщений (post), а асинхронные посылаются прямо в оконную процедуру (send).
Синхронными становятся сообщения, в основном, тогда, когда они являются результатом пользовательского ввода путем нажатия клавиш (например, WM_KEYDOWN и WM_KEYUP), это символы, введенные с клавиатуры
(WM_CHAR), результат движения мыши (WM_MOUSEMOVE) и щелчков кнопки мыши (WM_LBOTTONDOWN).
Кроме этого синхронные сообщения включают в себя сообщение от таймера (WM_TIMER), сообщение о необходимости плановой перерисовки (WM_PAINT) и сообщение о выходе из программы (WM_QUIT).
Сообщения становятся асинхронными во всех остальных случаях. Часто асинхронные сообщения являются результатом синхронных. При передаче асинхронного сообщения в DefWindowProc из оконной процедуры,
Windows часто обрабатывает сообщение, отправляя оконной процедуре другие асинхронные сообщения.
Очевидно, что процесс этот сложен, но к счастью большая часть сложностей ложится на Windows, а не на наши программы. С позиции оконной процедуры, эти сообщения проходят через нее упорядочено или синхронно.
Оконная процедура может что-то сделать с этими сообщениями, а может и проигнорировать их. По этой причине оконную процедуру назвали "конечным пунктом обработки" (ultimate hook). Сообщения извещают оконную процедуру почти обо всем, что влияет на окно.
Часто асинхронные сообщения являются результатом вызова определенных функций Windows или непосредственным результатом вызова функции SendMessage. (Кроме этого, сообщения могут помещаться в очередь сообщений посредством вызова функции PostMessage.)
Например, когда WinMain вызывает функцию CreateWindow, Windows создает окно и для этого отправляет оконной процедуре асинхронное сообщение WM_CREATE. Когда WinMain вызывает ShowWindow, Windows отправляет оконной процедуре асинхронные сообщения WM_SIZE и WM_SHOWWINDOW. Когда WinMain вызывает UpdateWindow, Windows отправляет оконной процедуре асинхронное сообщение WM_PAINT.
Сообщения не похожи на аппаратные прерывания. Во время обработки в оконной процедуре одного сообщения программа не может быть прервана другим сообщением. Только в том случае, если оконная процедура вызвала функцию, которая сама стала источником нового сообщения, то оконная процедура начнет обрабатывать это новое сообщение еще до того, как функция вернет управление программе.
1
Необходимо уточнить дальнейшее использование атрибутов сообщения "синхронное" и "асинхронное". Ставится сообщение в очередь или не ставится, определяется способом его отправки. В оригинальном тексте книги для указания конкретного способа отправки сообщения используются слова send (функция SendMessage) и post (функция PostMessage). Если для отправки сообщения используется функция
SendMessage, то оно не ставится в очередь, оконная процедура вызывается непосредственно, а функция возвращает управление только после обработки сообщения оконной процедурой. Если для отправки сообщения используется функция PostMessage, то оно ставится в очередь, а функция возвращает управление немедленно.
Таким образом, используя терминологию автора, можно сказать:
• если сообщение отправляется с помощью функции SendMessage, то оно является асинхронным;
• если сообщение отправляется с помощью функции PostMessage, то оно является синхронным.
(Прим. перев.).

39
Цикл обработки сообщений и оконная процедура работают не параллельно. Когда оконная процедура обрабатывает сообщение, то это результат вызова функции DispatchMessage в WinMain. DispatchMessage не завершается до тех пор, пока оконная процедура не обработала сообщение.
Но заметьте, что оконная процедура должна быть повторно-входимой (reentrant). Это означает, что Windows часто вызывает WndProc с новым сообщением, как результат вызова функции DefWindowProc в WndProc с предыдущим сообщением. В большинстве случаев повторная входимость оконной процедуры не создает проблем, но об этом следует знать.
Например, предположим, что вы вводите переменную в процесс обработки сообщения оконной процедурой и затем вызываете функцию Windows. Можете ли вы быть уверены в том, что после возврата функцией своего значения, ваша переменная осталась той же самой? Конечно, нет — в том случае, если конкретная функция
Windows, которую вы вызвали, стала источником другого сообщения, и оконная процедура изменила вашу переменную при обработке этого второго сообщения. Это одна из причин того, что при компиляции программ для
Windows необходимо отключать некоторые возможности оптимизации.
Часто возникает необходимость того, чтобы оконная процедура сохраняла информацию, полученную в сообщении, и использовала ее при обработке другого сообщения. Тогда эту информацию следует описывать в оконной процедуре в виде статических переменных, либо хранить в глобальных переменных.
Все вышеизложенное станет более понятным, когда вы прочитаете в следующих главах об оконных процедурах, способных обрабатывать большое число сообщений.
Думайте о ближнем
Windows 95 — это вытесняющая многозадачная среда. Это означает, что если программа работает слишком долго, то Windows может разрешить пользователю переключиться на другую программу. Это удобная вещь и одно из преимуществ Windows 95 по сравнению с предыдущими, основанными на DOS, версиями Windows.
Однако, поскольку Windows сконструирована определенным образом, эта вытесняющая многозадачность не всегда работает так, как бы вам хотелось. Например, предположим, что ваша программа тратит на обработку отдельного сообщения минуту или больше. Да, пользователь может переключиться на другую программу. Но пользователь ничего не в состоянии сделать с вашей программой. Пользователь не может переместить окно вашей программы, изменить его размер, закрыть его, т. е. вообще ничего. Так происходит потому, что, хотя ваша оконная процедура и должна выполнять все эти задачи, но она занята выполнением слишком долгой работы. Конечно, может показаться, что оконная процедура не выполняет операций по изменению размера и перемещению собственного окна, но, тем не менее, она это делает. Это часть работы функции DefWindowProc, которую, в свою очередь, необходимо считать частью оконной процедуры.
Если для вашей программы необходимо долго обрабатывать отдельные сообщения, то в главе 14 описан удобный способ сделать это. Даже при наличии вытесняющей многозадачности, не слишком хорошо оставлять ваше окно безвольно висящим на экране. Оно мешает пользователям, и они просто вашу программу будут ругать.
Кривая обучения
Да, как вы, несомненно, поняли из этой главы, программирование для Windows определенно отличается от программирования для общепринятой среды типа MS-DOS. Никто не станет утверждать, что программировать для
Windows легко.
При первом изучении программирования для Windows обычно делают то, что всегда делается при изучении новой операционной системы или нового языка программирования — пишется простая программа для вывода на экран содержимого файла. В общепринятой среде MS-DOS такая программа включает в себя обработку командной строки, простейший файловый Ввод/Вывод и форматирование вывода на экран. В отличии от этого, аналогичная программа для Windows превращается в монстра. Она требует изучения меню, окон диалога, полос прокрутки и т. д. Так как это ваша первая программа для Windows, то из-за необходимости сразу усвоить слишком большой объем материала, она обычно оказывается совершенно неправильной.
Когда работа над программой подходит к концу, она уже совершенно отличается от любой программы, которую вы когда-либо писали. Вместо командной строки для ввода имени файла, программа WINDUMP (назовем ее так) предлагает на экране список всех файлов текущего каталога. Вместо последовательной печати на экране содержимого файла, как это происходит в обычном телетайпе, WINDUMP имеет полосы прокрутки, и можно перемещаться в любую часть файла. И как особая награда, можно запустить две копии WINDUMP и сравнить два находящихся рядом файла. Короче говоря, из всех ранее написанных программ для вывода содержимого файла на экран WINDUMP первая, которой можно действительно гордиться.
Выясните для себя следующий вопрос: нужно ли, чтобы в ваших программах использовался современный и удобный пользовательский интерфейс, включающий в себя меню, окна диалога, полосы прокрутки и графику?

40
Если да, тогда еще один вопрос: хотите ли вы сами программировать все эти меню, окна диалога, полосы прокрутки и графику? Или было бы лучше воспользоваться уже подготовленными для этого программами
Windows? Другими словами, что проще: узнать, как использовать более 1000 функций, или писать их самому? Что проще: направить свои силы на изучение управляемой сообщениями архитектуры Windows, или бороться с разнообразными вариантами организации пользовательского ввода в традиционной модели программирования?
Если вы собираетесь создавать собственную логику интерфейса, вам было бы лучше закрыть эту книгу и заняться своим делом. Тем временем другим читателям предстоит в следующей главе выяснить, как вывести на экран окно с текстом и просмотреть там этот текст.
1   2   3   4   5   6   7   8   9   ...   41


База данных защищена авторским правом ©nethash.ru 2019
обратиться к администрации

войти | регистрация
    Главная страница


загрузить материал