Основы программирования в операционной системе windows

Работа по теме: Operatsionnye_sistemy. Глава: § 1.2. Основы программирования в ос Windows. ВУЗ: КНИТУ-КАИ.

Рассмотрев в
предыдущем параграфе многообразие
операционных систем для пер-

сональных компьютеров, можно сделать вывод, что программирование в конкретной

операционной
системе процесс настолько сложный,
основанный на специфических осо-

бенностях данной ОС, что переход программиста на новую операционную систему

практически соизмерим с переходом на
новый язык программирования. Поэтому в на-

стоящее время каждый программист является узким специалистом в области
програм-

мирования в
конкретной ОС, и даже более – в конкретном
узком направлении разработ-

ки для данной ОС. При этом общие принципы построения современных ОС

существенно не отличаются друг от
друга. Учебное пособие посвящено особенностям

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

курса невозможно качественно рассмотреть особенности технологий разработки сис-

28

темных элементов для многих ОС,
сосредоточимся в практическом плане
на операцион-

ных системах,
получивших в настоящее время наибольшее
распространение – ОС, раз-

работанных компанией
Microsoft – Windows линеек 9x и NT (2000, XP). Таким
образом,

в пособии
первоначально будут рассматриваться
особенности построения базовых эле-

ментов основных современных операционных систем, а далее в более практическом

плане особенности
разработки этих технологий в ОС Windows. В
качестве базового язы-

ка программирования
выбран язык С++ и инструментальная
система Visual C++ по сле-

дующим причинам [4]:

— ранее при изучении
основ программирования в качестве
базового был выбран имен-

но этот язык и
инструментальная система, таким образом
время, выделенное на изу-

чение курса
максимально будет потрачено на изучение
основ программирования ба-

зовых элементов ОС, а не на изучение
нового языка;

— операционные
системы семейства Windows сами были созданы
на языке программи-

рования С/С++, поэтому программирование их элементов на данном языке в наи-

меньшей вызовет проблемы совместимости
этих элементов;

— большая часть
документации, публикаций и других
материалов о программировании

для
Windows ориентирована на использование
C++, фактически вся документация

Microsoft рассчитана на программистов, знакомых именно с этим
языком програм-

мирования, который в настоящее время
«de facto» стал системным для многих
ОС, в

том числе и для Windows;

— в настоящее
время большая часть системных программистов
во всем мире работает

именно на С/С++.

Вместе с тем, в
рабочей среде Windows могут используются многие
другие попу-

лярные среды
разработки, В частности, получившая в
последнее время широкое распро-

странение, среда программирования Java
(в первую очередь, при программировании
для

Internet). Компания
Borland поддерживает собственную среду
Delphi, которая основана

на языке Pascal
(существует также C++ Builder, которая
использует похожий подход к

созданию приложений,
но ориентирована на использование
C++). Не стоит забывать и

про
Visual Basic (VB). Начиная с пятой версии, этот продукт
поддерживает полноцен-

ную компиляцию, технологию ActiveX, а также
поддерживает прямое обращение к API.

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

различные приложения
в среде Windows. В частности научились
создавать приложения

на базе однооконного
и многооконного интерфейса, обрабатывать
сообщения клавиату-

ры и мыши, создавать различные ресурсы,
предоставляемые ОС, использовать
цветовые

палитры,
создавать подключаемые библиотеки и
т.д. Все это поможет рассмотреть про-

граммирование
базовых системных элементов ОС в более
качественной форме. Перво-

начально кратко рассмотрим основы программирования в ОС семейства
Windows, об-

щие черты и различия Windows линеек 9x и NT.

1.2.1.
Принципы
взаимодействия
ОС Windows
с
прикладными
программами

Благодаря интерфейсу вызова функций в
Windows доступ к системным ресурсам

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

зывается
прикладным программным интерфейсом,
или API (Application Programming In-

terfase). Для
взаимодействия с Windows приложение
запрашивает функции API, с помо-

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

памяти, вывод на экран, создание окон и
т.п.

Поскольку
API состоит из большого числа функций,
может сложиться впечатле-

ние, что при компиляции
каждой программы, написанной для Windows,
к ней подклю-

чается код довольно
значительного объема. В действительности
это не так. Функции API

содержатся
в библиотеках динамической
загрузки
(Dynamic Link Libraries, или DLL),

29

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

т.е. при выполнении
программы. Рассмотрим, как осуществляется
механизм динамиче-

ской загрузки.

Динамическая загрузка обеспечивает
ряд существенных преимуществ.

Во-первых, поскольку практически все программы используют
API-функции, то

благодаря DLL-библиотекам существенно
экономится дисковое пространство,
которое в

противном случае занималось бы большим количеством повторяющегося кода, содер-

жащегося в каждом из исполняемых файлов.

Во-вторых, изменения и
улучшения в Windows-приложениях сводятся
к обновле-

нию только
содержимого DLL-библиотек. Уже существующие
тексты программ не тре-

буют перекомпиляции.

В настоящее время
наибольшее распространение получила
версия API, которая по-

лучила название
Win32. Данная версия API пришла на смену
версии Win16, используе-

мой в Windows 3.1.
Фактически 32-разрядная Win32, используемая
в операционных сис-

темах 9x, является надмножеством для
Win16 (т.е. фактически включает в себя этот

интерфейс), так
как большинство функций имеет то
же название и применяется анало-

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

ются друг от друга. Win32 поддерживает
32-разрядную линейную адресацию, тогда
как

Win16 работает только
с 16-разрядной сегментированной моделью
памяти. Это привело

к тому,
что некоторые функции были модифицированы таким образом, чтобы прини-

мать
32-разрядные аргументы и возвращать
32-разрядные значения. Часть из них при-

шлось изменить с учетом
32-разрядной архитектуры. Была реализована
поддержка по-

токовой многозадачности, новых элементов интерфейса и прочих нововведений

Windows.

Так как Win32
поддерживает полностью 32-разрядную
адресацию, то логично, что

целые типы данных
(intergers) также объявлены 32-разрядными.
Это означает, что пере-

менные типа int и
unsigned будут иметь длину 32 бита, а не 16,
как в Windows 3.1. Если

же необходимо использовать переменную или константу длиной
16 бит, они должны

быть объявлены
как short (далее будет показано, что для
этих типов определены незави-

симые typedef-имена.).
Следовательно, при переносе программного
кода из 16-разрядной

среды необходимо
убедиться в правильности использования
целочисленных элементов,

которые автоматически
будут расширены до 32 битов, что может
привести к появлению

побочных эффектов.

Другим следствием
32-разрядной адресации является то, что
указатели больше не

нужно объявлять
как near и far. Любой указатель может получить
доступ к любому уча-

стку памяти. В Windows 9x константы near и far
объявлены (с помощью директивы #de-

fine) пустыми.

Существенные
изменения коснулись также функций API,
которые работают с сим-

вольными
строками. Внутренние механизмы Windows 9x
(не полностью), NT и Windows

2000 используют символы в формате
UNICODE (16-битное значение символов). Но

многие прикладные
программы продолжают использовать ANSI
(то есть стандарт, в ко-

тором каждый символ
кодируется при помощи 8 бит). Чтобы решить
эту проблему, каж-

дая функция,
работающая со строками, реализована в
двух вариантах. В первом вариан-

те в качестве аргумента воспринимается строка в формате
ANSI (внутри функции эта

строка автоматически
преобразуется в UNICODE). Второй вариант
функции напрямую

работает со строками в формате UNICODE.

Одним из подмножеств API является GDI
(Graphics Device Interfase – интерфейс

графического устройства). GDI – это та
часть Windows, которая обеспечивает поддержку

аппаратно-независимой
графики. Благодаря функциям GDI
Windows-приложение может

выполняться на различных ПЭВМ.

30

Еще одной особенность Windows является
многозадачность, причем поддержива-

ется два
типа многозадачности: основанная на
процессах и основанная на потоках. Да-

лее идеология многозадачности будет
рассмотрена более подробно.

Во многих операционных
системах взаимодействие между системой
и программой

инициализирует программа. Например, в
DOS программа запрашивает разрешение на

ввод и вывод данных. Говоря другими
словами, не- Windows-программы сами вызывают

операционную
систему. Обратного процесса не происходит.
В Windows все совершенно

наоборот:
именно система вызывает программу. Это
осуществляется следующим обра-

зом: программа ожидает получения сообщения от
Windows. Когда это происходит, то

выполняется
некоторое действие. После его завершения
программа ожидает следующе-

го сообщения.

Windows может посылать
программе сообщения множества различных
типов. На-

пример, каждый
раз при щелчке мышью в окне активной программы
посылается соот-

ветствующее сообщение. Другой тип сообщений посылается, когда необходимо обно-

вить содержимое
активного окна. Сообщения посылаются
также при нажатии клавиши,

если программа
ожидает ввода с клавиатуры. Необходимо
запомнить одно: по отноше-

нию к программе сообщения появляются случайным образом. Вот почему
Windows-

программы похожи
на программы обработки прерываний:
невозможно предсказать, ка-

кое сообщение появиться в следующий
момент.

Функция окна. Все
Windows-программы должны содержать специальную функ-

цию, которая не
используется в самой программе, но
вызывается самой операционной

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

вызывается
Windows, когда системе необходимо передать сообщение в программу.

Именно через нее осуществляется взаимодействие между программой и системой.

Функция окна передает сообщение в своих аргументах. Согласно терминологии
Win-

dows, функции,
вызываемые системой, называются функциями
обратного
вызова.
Та-

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

общения от Windows,
функция окна должна вызывать выполнение
действия, указанного

в сообщении.
Конечно, программа не обязана отвечать на
все сообщения, посылаемые

Windows. Поскольку их
могут быть сотни, то большинство сообщений
обычно обраба-

тывается самой
системой, а программе достаточно поручить
Windows выполнить дейст-

вия, предусмотренные по умолчанию.

Цикл сообщений. Как объяснялось выше,
Windows взаимодействует с програм-

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

ваемый цикл
сообщений. В этом цикле каждое
необработанное сообщение должно быть

извлечено из
очереди сообщений данного приложения
и передано назад в Windows, ко-

торая затем вызывает
функцию окна программы с данным сообщением
в качестве аргу-

мента.

Класс
окна.
Как будет показано далее, каждое окно
в Windows-приложении харак-

теризуется определенными атрибутами, называемыми классом окна.
(Здесь понятие

“класс” не идентично используемому в С++. Оно,
скорее, означает стиль или тип.) В

традиционной
программе класс окна должен быть
определен и зарегистрирован прежде,

чем будет создано окно. При регистрации необходимо сообщить
Windows, какой вид

должно иметь окно
и какую функцию оно выполняет. В то же
время регистрация класса

окна еще не
означает создание самого окна.
Для этого требуется выполнить дополни-

тельные действия.

Структура
Windows-программ отличается от структуры программ других типов.

Это вызвано двумя
обстоятельствами: во-первых, способом
взаимодействия между про-

граммой и Windows,
описанным выше; во-вторых, правилами,
которым следует подчи-

няться для создания
стандартного интерфейса Windows-приложения
(т.е. чтобы сделать

программу “похожей “ на Windows-приложение).

31

Цель Windows – дать человеку, который хотя
бы немного знаком с системой, воз-

можность сесть за
компьютер и запустить любое приложение
без предварительной под-

готовки. Для этого
Windows предоставляет дружественный
интерфейс пользователя, ко-

торый необходимо поддерживать всем программистам, создающим программное

обеспечение в данной операционной
системе.

1.2.2. Типы
данных
в Windows

В Windows-программах
не слишком широко применяются
стандартные типы дан-

ных из С или С++,
такие как int или char*. Вместо них используются
типы данных, оп-

ределенные в
различных библиотечных (header) файлах.
Наиболее часто используемыми

типами являются
[11]: HANDLE, HWND, BYTE, WORD, DWORD, UNIT, LONG, BOOL,

LPSTR и LPCSTR.

Тип HANDLE обозначает
32-разрядное целое, используемое в
качестве дескрипто-

ра. Есть несколько похожих типов данных, но все они имеют ту же длину, что и

HANDLE,
и начинаются с литеры Н. Дескриптор
– это просто число, определяющее не-

который ресурс.

Тип HWND обозначает
32-разрядное целое – дескриптор окна.
В программах, ис-

пользующих
библиотеку MFC, дескрипторы применяются
не столь широко, как это име-

ет место в традиционных программах.

Тип BYTE обозначает 8-разрядное беззнаковое
символьное значение.

Тип WORD – 16-разрядное беззнаковое короткое
целое.

Тип DWORD – беззнаковое длинное
целое.

Тип UINT — беззнаковое 32-разрядное целое.

Тип LONG эквивалентен типу long.

Тип BOOL обозначает
целое и используется, когда значение
может быть либо ис-

тинным, либо ложным.

Тип LPSTR определяет указатель на строку.

Тип LPCSTR – константный
(const) указатель на строку.

1.2.3.
Графический
и
консольный
интерфейс

В Windows поддерживается
два типа приложений: основанные на
графическом ин-

терфейсе
(graphical user interface, GUI) и
консольные
(console user interface, CUI) У
при-

ложений первого
типа внешний интерфейс чисто графический
— создаются окна, меню,

диалоговые окна и т.д. Почти все стандартные программы
Windows и большинство

пользовательских программ являются
GUI — приложениями. Приложения консольного

типа работают
в текстовом режиме: они не формируют
окна, не обрабатывают сообще-

ния, но на экране
тоже размещаются в окне, правда черного
цвета и могут вызывать диа-

логовые окна.

Все Windows-программы,
написанные на языке C++ начинают выполнение
с вызо-

ва функции входа. В среде Win32 существует
четыре модификации таких функций:

int WINAPI WinMain(

HINSTANCE hInstance, //дескриптор, присваиваемый
запущенному приложению

HINSTANCE hPrevInstance, //для совместимости с
win16, в win32 не используется

LPSTR lpCmdLine, //указатель на командную строку,
если приложение так запущено

int nCmdShow ); //значение, которое
может быть передано в функцию ShowWindow()

int WINAPI wWinMain(HINSTANCE hInstance,

HINSTANCE hPrevInstance,

32

LPSTR lpCmdLine,

int nCmdShow);

int _ _cdecl main( int argc, //количество аргументов,
переданных в командной строке

char *argv[], //массив размером
argc
с указателями на ANSI-строки.

//Каждый элемент массива указывает на
один из аргументов командной строки

char *envp[] ); //массив указателей на ANSI-строки.
Каждый элемент массива

// указывает на строку — переменную
окружения (область памяти,

// выделенная в адресном пространстве
процесса)

int _ _cdecl wmain( int argc, //количество аргументов,
переданных в командной строке

wchar_t *argv[], //массив размером
argc
с указателями на UNICODE-строки.

//Каждый элемент массива указывает на
один из аргументов командной строки

wchar_t *envp[] );//массив указателей на
UNICODE-строки. Каждый элемент массива

//указывает на строку — переменную
окружения (область памяти, выделенная

//в адресном пространстве процесса)

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

терфейсом
(GUI), две другие — при консольном
(CUI). Приставка w перед названием

функции показывает,
что функция используется для работы с
Unicode — строками, обыч-

ные функции работают
с ANSI — строками. На самом деле входная
функция операцион-

ной системой не
вызывается, вместо этого происходит
обращение к стартовой функции

из библиотеки С/С++. Она инициализирует библиотеку С/С++, а также обеспечивает

корректное создание
любых объявленных глобальных и статических
объектов до того,

как начнется
выполнение программного кода. В таблице
1.1 показано, в каких случаях

реализуются различные функции входа
[7].

Таблица
1.1

Тип приложения

GUI-приложение, работающее с

ANSI-символами и строками

GUI-приложение,
работающее с

Unicode-символами и строками

CUI-приложение, работающее с

ANSI-символами и строками

CUI-приложение, работающее с

Unicode-символами и строками

Функция

входа

WinMain()

wWinMain()

Main()

Wmain()

Стартовая функция, встраиваемая в

исполняемый
файл

WinMainCRTStartup()

wWinMainCRTStartup()

mainCRTStartup()

wmainCRTStartup()

В целом все стартовые функции выполняют
следующие однотипные задачи:

• считывают
указатель на полную командную строку
нового процесса;

• считывают
указатель на переменные окружения
нового процесса;

• инициализируют
глобальные переменные из библиотеки
С/С++

• инициализируют кучу
(динамически распределяемую область памяти), используе-

мую
С-функциями выделения памяти (malloc,
calloc)
и
другими процедурами низко-

уровневого ввода-вывода;

• вызывают
конструкторы всех глобальных и
статических объектов С++-классов.

Закончив эти операции, стартовая функция
обращается к функции входа.

1.2.4.
Создание
элементарного
графического
окна

33

Рассмотрим, как создать элементарное
графическое окно в Windows. Как уже ука-

зывалось выше, главной функцией для разработки такой программы будет
WinMain().

Сразу после входа
в WinMain() создается и регистрируется класс
главного окна прило-

жения. Для этого
необходимо заполнить структуру WNDCLASS.
Для создания окна ис-

пользуются функции
CreateWindow() непосредственно для его создания,
ShowWindow()

для визуализации и UpdateWindow() для перерисовки.
Все эти функции описаны в файле

windows.h. Рассмотрим структуру
WNDCLASS и наиболее значимую функцию Create-

Window() более подробно.

Структура WNDCLASS состоит из 10 полей:

typedef struct tagWNDCLASS

{

UINT cbSize; //размер структуры в байтах

UINT style; //стиль класса окна

WNDPROC lpfnWndProc; // указатель на функцию окна

HINSTANCE hInstance; //дескриптор приложения,
которое запускает окно

HICON hIcon; //дескриптор пиктограммы

HCURSOR hCursor; // дескриптор курсора

HBRUSH hbrBackground; // дескриптор кисти для
закраски фона

LPCTSTR lpszMenuName; //указатель на строку с
именем меню

LPCTSTR lpszClassName; // указатель на строку с
именем класса

HICON hIconSm; //дескриптор малой пиктограммы

} WNDCLASS;

Функция CreateWindow() получает на входе 11
параметров:

HWND CreateWindow (

LPCTSTR lpClassName,
// зарегистрированное имя класса

LPCTSTR lpWindowName,
// имя окна

DWORD dwStyle,
// стиль окна

int x,
// x -координата позиции окна

int y,
// y -координата позиции окна

int nWidth
, // ширина окна

int nHeight
, // высота окна

HWND hWndParent
, // указатель родительского окна

HMENU hMenu
, // дескриптор меню, NULL если меню нет

HINSTANCE hInstance
, // дескриптор приложения

LPVOID lParam
);// данные, которые могут быть переданы
создания окна

// NULL если данные не передаются

Для примера
рассмотрим исходный текст программы,
которая создает элементар-

ное
окно
в
Windows.

#include «windows.h»

#include «windowsx.h»

// функция для управления параметрами
создания и уничтожения главного окна

// запуск всех дополнительных элементов
окна (если они есть) производится в ней

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

// главная
функция

34

WINAPI WinMain(

HINSTANCE hInstance,

HINSTANCE hPrevInstance,

LPSTR lpCmdLine,

int nCmdShow)

{

// описываем параметры, которые необходимы
для создания окна

HWND hwnd; // указатель класса окна

MSG msg; // Структура MSG содержит информацию

//о сообщениях из очереди потока сообщений.

WNDCLASS w; // структура определяющая класс
окна

memset(&w,0,sizeof(WNDCLASS)); //выделение памяти
для класса окна

w.style = 0; // стиль окна по умолчанию

w.lpfnWndProc = WndProc; // функция окна

w.hInstance = hInstance; // дескриптор приложения

w.hbrBackground =
GetStockBrush(WHITE_BRUSH); // цвет
для

//заполнения
окна

w.lpszClassName = «API
Windows»;// имя
класса
окна

RegisterClass(&w); //Регистрируем класс окна

// Создаем окно

hwnd = CreateWindow(«API
Windows»,//имя
класса

«API Windows»,
//название
окна

WS_OVERLAPPEDWINDOW,// стиль
окна

10,//x — координата

10,//y- координата

600,//ширина

480,//высота

NULL,//нет родительского окна

NULL,//нет меню

hInstance,//дескриптор приложения

NULL);//не передаем данных

ShowWindow(hwnd,nCmdShow);//активация окна

UpdateWindow(hwnd);//перерисовка

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

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

{

TranslateMessage(&msg); // разрешить использование
клавиатуры

DispatchMessage(&msg); // вернуть управление
Windows

}

//возвращаемое значении для функции
WndProc()

return msg.wParam;

}

LRESULT CALLBACK WndProc(HWND hwnd, UINT Message,

WPARAM wparam,LPARAM lparam)

{

if (Message == WM_DESTROY )

{

PostQuitMessage(0);

return 0;

}

35

}

return DefWindowProc(hwnd,Message,wparam,lparam);

Компиляция и компоновка проекта приводит
к созданию exe-файла, запуск которо-

го приводит к появлению окна,
представленного на рис.1.1.

Рис.1.1.
Результат
выполнения
программы

1.2.5.
Сходства
и
различия Windows 9x
и Windows NT (2000, XP)

Общие
черты.

1. Простота использования:

• Автоматическое
определение оборудования во время
инсталляции и последую-

щей конфигурации

• Интерфейс
пользователя нового поколения

• Технология
Plug and Play

2.Мощность:

• Реальная
многозадачность

• Использование
Win32® и OLE

3. Способность совместной работы:

• Встроенная
поддержка основных сетевых протоколов
(TCP/IP, IPX/SPX, NetBeui)

• Открытая сетевая архитектура и способность выбора типа сетевого клиента,

транспорта, драйверов и расширяемость
для поддержки дополнительных сетевых

приложений.

• Встроенные
средства предоставления удаленного
доступа к рабочей станции

4. Управляемость:

• Открытая
архитектура управления с возможностью
использования дополнитель-

ных управляющих систем.

• Поддержка
стандартов системного управления

• Независимая настройка рабочей области экрана для каждого пользователя и

средства мониторинга.

5. Поддержка приложений:

• Возможность
выполнения 16-разрядных Windows приложений

36

• Возможность
выполнения 32-разрядных Windows приложений
и поддержка OLE

2.0

6. Переносимость

• Полная
переносимость на платформы Intel® (386DX,
486, и Pentium® и т.д.)

РазличияWindows 9x и Windows NT приведены
в таблице 1.2.

Таблица
1.2

Свойство

Windows 9x Windows NT

Обеспечение полной изоляции 16 битных
Windows —

приложений путем предоставления каждому
отдель-

ного адресного пространства

Предоставление защиты по стандарту
C-2. Отдельные

каталоги и файлы могут быть сделаны
невидимыми

для выбранных пользователей.

Установка прав различных пользователей
к измене-

нию конфигурации системы.

Защита файловой структуры

нет

нет

нет

нет

да

да

да

да

Автоматическое восстановление после
краха системы нет

Поддержка
приложений

да

MS DOS

поддержка файловых структур NTFS и HPFS

Поддержка графической библиотеки
OpenGL (трех-

мерная графика)

Выполнение IBM® Presentation Manager® (through

1.3) и поддержка стандарта POSIX 1003.2

да

нет

да, начиная с

Windows 98

нет

большинство

да

да

да

Драйверы MS DOS

Драйверы Win16

Компрессия диска

Системные
возможности.

да

да

да

нет

нет

да

Поддержка мультипроцессорных платформ

нет

да

Техническая
поддержка
и
сервис

Команды инженеров для решения проблем
в критиче- нет

ских случаях

да

Ежемесячные информационные выпуски

нет

да

Ежеквартальные сервисные пакеты на CD
и дискетах нет

Введение.

  • Некоторые сведения о программировании Windows-приложений
  • Программная среда Windows
  • Интерфейс вызовов функций в Windows
  • Библиотеки динамической загрузки (DLL)
  • Win16 или Win32
  • Интерфейс GDI
  • Многозадачность в Windows
  • Взаимодействие программ и Windows
  • Основы программирования под Windows
  • Функция WinMain()
  • Функция окна
  • Цикл сообщений
  • Класс окна
  • Типы данных в Windows
  • Преимущества использования MFC
  • Обзор среды Microsoft Developer Studio
  • Библиотека MFC
  • Архитектура приложения
  • Каркас приложения
  • При определении производного класса программист может:
  • Каркас приложений
  • Проект приложения
  • Использование средств разработки
  • Типы мастеров проектов
  • Преимущества мастеров проектов
  • Обзор возможностей ClassWizard
  • Имена, используемые в MFC

Эти главы являются некоторым пособием для тех, кто хочет познакомиться с языком программирования C++ и Visual C++.

В связи с тем, что сегодня уровень сложности программного обеспечения очень высок, разработка приложений Windows с использованием только какого-либо языка программирования (например, языка C) значительно затрудняется. Программист должен затратить массу времени на решение стандартных задач по созданию многооконного интерфейса. Реализация технологии связывания и встраивания объектов — OLE — потребует от программиста еще более сложной работы.

Чтобы облегчить работу программиста практически все современные компиляторы с языка C++ содержат специальные библиотеки классов. Такие библиотеки включают в себя практически весь программный интерфейс Windows и позволяют пользоваться при программировании средствами более высокого уровня, чем обычные вызовы функций. За счет этого значительно упрощается разработка приложений, имеющих сложный интерфейс пользователя, облегчается поддержка технологии OLE и взаимодействие с базами данных.

Современные интегрированные средства разработки приложений Windows позволяют автоматизировать процесс создания приложения. Для этого используются генераторы приложений. Программист отвечает на вопросы генератора приложений и определяет свойства приложения — поддерживает ли оно многооконный режим, технологию OLE, трехмерные органы управления, справочную систему. Генератор приложений, создаст приложение, отвечающее требованиям, и предоставит исходные тексты. Пользуясь им как шаблоном, программист сможет быстро разрабатывать свои приложения.

Подобные средства автоматизированного создания приложений включены в компилятор Microsoft Visual C++ и называются MFC AppWizard. Заполнив несколько диалоговых панелей, можно указать характеристики приложения и получить его тексты, снабженные обширными комментариями. MFC AppWizard позволяет создавать однооконные и многооконные приложения, а также приложения, не имеющие главного окна, -вместо него используется диалоговая панель. Можно также включить поддержку технологии OLE, баз данных, справочной системы.

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

Нужно отметить, что MFC AppWizard создает тексты приложений только с использованием библиотеки классов MFC (Microsoft Foundation Class library). Поэтому только изучив язык C++ и библиотеку MFC, можно пользоваться средствами автоматизированной разработки и создавать свои приложения в кратчайшие сроки.

Некоторые сведения о программировании Windows-приложений

MFC — это базовый набор (библиотека) классов, написанных на языке С++ и предназначенных для упрощения и ускорения процесса программирования под Windows. Перед изучением библиотеки MFC и ее использованием для создания Windows-приложений, следует вспомнить, как работает сама Windows и каковы принципы взаимодействия программ с ней, какова структура типичной Windows-программы.

Программная среда Windows

Рассмотрим наиболее важные моменты работы Windows и принципы взаимодействия программ с ней.

Интерфейс вызовов функций в Windows

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

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

Библиотеки динамической загрузки (DLL)

Поскольку API состоит из большого числа функций, может сложиться впечатление, что при компиляции каждой программы, написанной для Windows, к ней подключается код довольно значительного объема. В действительности это не так. Функции API содержатся в библиотеках динамической загрузки (Dynamic Link Libraries, или DLL), которые загружаются в память только в тот момент, когда к ним происходит обращение, т.е. при выполнении программы. Рассмотрим, как осуществляется механизм динамической загрузки.

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

Win16 или Win32

В настоящее время широко распространены две версии API. Первая называется Win16 и представляет собой 16-разрядную версию, используемую в Windows 3.1. Вторая, 32-разрядная версия, называется Win32 и используется в Windows 95 и Windows NT. Win32 является надмножеством для Win16 (т.е. фактически включает в себя этот интерфейс), так как большинство функций имеет то же название и применяется аналогичным образом. Однако, будучи в принципе похожими, оба интерфейса все же отличаются друг от друга. Win32 поддерживает 32-разрядную линейную адресацию, тогда как Win16 работает только с 16-разрядной сегментированной моделью памяти. Это привело к тому, что некоторые функции были модифицированы таким образом, чтобы принимать 32-разрядные аргументы и возвращать 32-разрядные значения. Часть из них пришлось изменить с учетом 32-разрядной архитектуры. Была реализована поддержка потоковой многозадачности, новых элементов интерфейса и прочих нововведений Windows.

Так как Win32 поддерживает полностью 32-разрядную адресацию, то логично, что целые типы данных (intergers) также объявлены 32-разрядными. Это означает, что переменные типа int и unsignerd будут иметь длину 32 бита, а не 16, как в Windows 3.1. Если же необходимо использовать переменную или константу длиной 16 бит, они должны быть объявлены как short. (дальше будет показано, что для этих типов определены независимые typedef-имена.) Следовательно, при переносе программного кода из 16-разрядной среды необходимо убедиться в правильности использования целочисленных элементов, которые автоматически будут расширены до 32 битов, что целочисленных элементов, которые автоматически будут расширены до 32 битов, что может привести к появлению побочных эффектов.

Другим следствием 32-разрядной адресации является то, что указатели больше не нужно объявлять как near и far. Любой указатель может получить доступ к любому участку памяти. В Windows 95 и Windows NT константы near и far объявлены (с помощью директивы #define)пустыми.

Интерфейс GDI

Одним из подмножеств API является GDI (Graphics Device Interfase — интерфейс графического устройства). GDI — это та часть Windows, которая обеспечивает поддержку аппаратно-независимой графики. Благодаря функциям GDI Windows-приложение может выполняться на самых различных компьютерах.

Многозадачность в Windows

Как известно, все версии Windows поддерживают многозадачность. В Windows 3.1 имеется только один тип многозадачности — основанный на процессах. В более передовых системах, таких как Windows 95 и Windows NT, поддерживается два типа многозадачности: основанный на процессах и основанный на потоках. Давайте рассмотрим их чуть подробнее.

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

Поток — это отдельная часть исполняемого кода. Название произошло от понятия «направление протекания процесса». В многозадачности данного типа отдельные потоки внутри одного процесса также могут выполняться одновременно. Все процессы имеют по крайней мере один поток, но в Windows 95 и Windows NT их может быть несколько.

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

Есть и другое существенное различие между многозадачностями Windows 3.1 и Windows 95/NT. В Windows 3.1 используется неприоритетная многозадачность. Это означает, что процесс, выполняющийся в данный момент, получает доступ к ресурсам центрального процессора и удерживает их в течение необходимого ему времени. Таким образом, неправильно выполняющаяся программа может захватить все ресурсы процессора и не давать выполняться другим процессам. В отличие от этого в Windows 95 и Windows NT используется приоритетная многозадачность. В этом случае каждому активному потоку предоставляется определенный промежуток времени работы процессора. По истечению данного промежутка управление автоматически передается следующему потоку. Это не дает возможность программам полностью захватывать ресурсы процессора. Интуитивно должно быть понятно, что такой способ более предпочтителен.

Взаимодействие программ и Windows

Во многих операционных системах взаимодействие между системой и программой инициализирует программа. Например, в DOS программа запрашивает разрешение на ввод и вывод данных. Говоря другими словами, не- Windows-программы сами вызывают операционную систему. Обратного процесса не происходит. В Windows все совершенно наоборот: именно система вызывает программу. Это осуществляется следующим образом: программа ожидает получения сообщения от Windows. Когда это происходит, то выполняется некоторое действие. После его завершения программа ожидает следующего сообщения.

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

Основы программирования под Windows

Поскольку архитектура Windows-программ основана на принципе сообщений, все эти программы содержат некоторые общие компоненты. Обычно их приходится в явном виде включать в исходный код. Но, к счастью, при использовании библиотеки MFC это происходит автоматически; нет необходимости тратить время и усилия на их написание. Тем не менее, чтобы до конца разобраться, как работает Windows-программа, написанная с использованием MFC, и почему она работает именно так, необходимо в общих чертах понять назначение этих компонентов.

Функция WinMain()

Все Windows-программы начинают выполнение с вызова функции WinMain(). При традиционном методе программирования это нужно делать явно. С использованием библиотеки MFC такая необходимость отпадает, но функция все-таки существует.

Функция окна

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

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

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

Цикл сообщений

Как объяснялось выше, Windows взаимодействует с программой, посылая ей сообщения. Все приложения Windows должны организовать так называемый цикл сообщений (обычно внутри функции WinMain()). В этом цикле каждое необработанное сообщение должно быть извлечено из очереди сообщений данного приложения и передано назад в Windows, которая затем вызывает функцию окна программы с данным сообщением в качестве аргумента. В традиционных Windows-программах необходимо самостоятельно создавать и активизировать такой цикл. При использовании MFC это также выполняется автоматически. Однако важно помнить, что цикл сообщений все же существует. Он является неотъемлемой частью любого приложения Windows.

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

Класс окна

Как будет показано дальше, каждое окно в Windows-приложении характеризуется определенными атрибутами, называемыми классом окна. (Здесь понятие «класс» не идентично используемому в С++. Оно, скорее, означает стиль или тип.) В традиционной программе класс окна должен быть определен и зарегистрирован прежде, чем будет создано окно. При регистрации необходимо сообщить Windows, какой вид должно иметь окно и какую функцию оно выполняет. В то же время регистрация класса окна еще не означает создание самого окна. Для этого требуется выполнить дополнительные действия. При использовании библиотеки MFC создавать собственный класс окна нет необходимости. Вместо этого можно работать с одним из заранее определенных классов, описанных в библиотеке. В этом еще одно ее преимущество.

Специфика программ для Windows

Структура Windows-программ отличается от структуры программ других типов. Это вызвано двумя обстоятельствами: во-первых, способом взаимодействия между программой и Windows, описанным выше; во-вторых, правилами, которым следует подчиняться для создания стандартного интерфейса Windows-приложения (т.е. чтобы сделать программу «похожей » на Windows-приложение).

Цель Windows — дать человеку, который хотя бы немного знаком с системой, возможность сесть за компьютер и запустить любое приложение без предварительной подготовки. Для этого Windows предоставляет дружественный интерфейс пользователя. Теоретически, если пользователь сумел запустить одно Windows-приложение, то он сумеет запустить и любое другое. Конечно, на практике придется немного потренироваться, чтобы научиться использовать большинство программ с максимальной эффективностью. Однако это связано исключительно с тем, что программа делает, а не с тем, как ею пользоваться. Ведь, фактически, значительная часть кода Windows-приложения предназначена именно для организации интерфейса с пользователем.

Хотя создание удобного интерфейса «под Windows» является основной задачей при написании любой Windows-программы, такой интерфейс не создается автоматически. То есть вполне можно написать программу, в которой элементы интерфейса используются неэффективно. Чтобы этого избежать, необходимо целенаправленно применять методику, описанную в данной книге. Только программы, написанные таким способом, будут выглядеть и работать действительно так, как надлежит Windows-программам.

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

Типы данных в Windows

В Windows-программах вообще (и в использующих библиотеку MFC в частности) не слишком широко применяются стандартные типы данных из С или С++, такие как int или char*. Вместо них используются типы данных, определенные в различных библиотечных (header) файлах. Наиболее часто используемыми типами являются HANDLE, HWND, BYTE, WORD, DWORD, UNIT, LONG, BOOL, LPSTR и LPCSTR. Тип HANDLE обозначает 32-разрядное целое, используемое в качестве дескриптора. Есть несколько похожих типов данных, но все они имеют ту же длину, что и HANDLE, и начинаются с литеры Н. Дескриптор — это просто число, определяющее некоторый ресурс. Например, тип HWND обозначает 32-разрядное целое — дескриптор окна. В программах, использующих библиотеку MFC, дескрипторы применяются не столь широко, как это имеет место в традиционных программах. Тип BYTE обозначает 8-разрядное беззнаковое символьное значение, тип WORD — 16-разрядное беззнаковое короткое целое, тип DWORD — беззнаковое длинное целое, тип UNIT — беззнаковое 32-разрядное целое. Тип LONG эквивалентен типу long. Тип BOOL обозначает целое и используется, когда значение может быть либо истинным, либо ложным. Тип LPSTR определяет указатель на строку, а LPCSTR — константный (const) указатель на строку.

Преимущества использования MFC

Как уже упоминалось, MFC — это базовый набор (библиотека) классов, написанных на языке С++ и предназначенных для упрощения и ускорения процесса программирования для Windows. Библиотека содержит многоуровневую иерархию классов, насчитывающую около 200 членов. Они дают возможность создавать Windows-приложения на базе объектно-ориентированного подхода. С точки зрения программиста, MFC представляет собой каркас, на основе которого можно писать программы для Windows.

Библиотека MFC разрабатывалась для упрощения задач, стоящих перед программистом. Как известно, традиционный метод программирования под Windows требует написания достаточно длинных и сложных программ, имеющих ряд специфических особенностей. В частности, для создания только каркаса программы таким методом понадобится около 75 строк кода. По мере же увеличения сложности программы ее код может достигать поистине невероятных размеров. Однако та же самая программа, написанная с использованием MFC, будет примерно в три раза меньше, поскольку большинство частных деталей скрыто от программиста.

Одним из основных преимуществ работы с MFC является возможность многократного использования одного и того же кода. Так как библиотека содержит много элементов, общих для всех Windows-приложений, нет необходимости каждый раз писать их заново. Вместо этого их можно просто наследовать (говоря языком объектно-ориентированного программирования). Кроме того, интерфейс, обеспечиваемый библиотекой, практически независим от конкретных деталей, его реализующих. Поэтому программы, написанные на основе MFC, могут быть легко адаптированы к новым версиям Windows (в отличие от большинства программ, написанных обычными методами).

Еще одним существенным преимуществом MFC является упрощение взаимодействия с прикладным программным интерфейсом (API) Windows. Любое приложение взаимодействует с Windows через API, который содержит несколько сот функций. Внушительный размер API затрудняет попытки понять и изучить его целиком. Зачастую даже сложно проследить, как отдельные части API связанны друг с другом! Но поскольку библиотека MFC объединяет (путем инкапсуляции) функции API в логически организованное множество классов, интерфейсом становится значительно легче управлять.

Поскольку MFC представляет собой набор классов, написанных на языке С++, поэтому программы, написанные с использованием MFC, должна быть в то же время программами на С++. Для этого необходимо владеть соответствующими знаниями. Для начала необходимо уметь создавать собственные классы, понимать принципы наследования и уметь переопределять виртуальные функции. Хотя программы, использующие библиотеку MFC, обычно не содержат слишком специфических элементов из арсенала С++, для их написания тем не менее требуются солидные знания в данной области.

Замечание. Небольшое число классов, определенных в библиотеке, не связанно непосредственно с программированием под Windows. Это, в частности, классы, предназначенные для создания строк, управления файлами и обработки особых ситуаций. Иногда называемые классами общего назначения, они могут использоваться как Windows-, так и не- Windows-приложениями.

Обзор среды Microsoft Developer Studio

Студия разработчика фирмы Microsoft (Microsoft Developer Studio) — это интегрированная среда для разработки, позволяющая функционировать различным средам разработки, одна из которых Visual C++, другая — Visual J++. В дальнейшем будет идти речь только о среде разработки Visual C++.

В студии разработчика можно строить обычные программы на C и С++, создавать статические и динамические библиотеки, но основным режимом работы является создание Windows-приложений с помощью инструмента MFC AppWizard (Application Wizard — мастер приложений) и библиотеки базовых классов MFC (Microsoft Foundation Class Library). Такие приложения называются MFC-приложениями. Главная особенность этих Windows-приложений состоит в том, что они работают как совокупность взаимодействующих объектов, классы которых определены библиотекой MFC.

Библиотека MFC

Главная часть библиотеки MFC состоит из классов, используемых для построения компонентов приложения. С каждым MFC-приложением связывается определяющий его на верхнем уровне объект theApp, принадлежащий классу, производному от CWinApp.

Как правило, структура приложения определяется архитектурой Document-View (документ-облик). Это означает, что приложение состоит из одного или нескольких документов — объектов, классы которых являются производными от класса CDocument (класс «документ»). С каждым из документов связаны один или несколько обликов — объектов классов, производных от CView (класс «облик «) и определяющих облик документа.

Класс CFrameWnd («окна-рамки») и производные от него определяют окна-рамки на дисплее. Элементы управления, создаваемые при проектировании интерфейса пользователя, принадлежат семейству классов элементов управления. Появляющиеся в процессе работы приложения диалоговые окна — это объекты классов, производных от CDialog.

Классы CView, CFrameWnd, CDialog и все классы элементов управления наследуют свойства и поведение своего базового класса CWnd («окно»), определяющего по существу Windows-окно. Этот класс в свою очередь является наследником базового ласса CObject («объект»).

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

Архитектура приложения

У всех Windows-приложений фиксированная структура, определяемая функцией WinMain. Структура приложения, построенного из объектов классов библиотеки MFC, является еще более определенной.

Приложение состоит из объекта theApp, функции WinMain, и некоторого количества других объектов. Сердцевина приложения — объект theApp — отвечает за создание всех остальных объектов и обработку очереди сообщений. Объект theApp является глобальным и создается еще до начала работы функции WinMain. Работа функции WinMain заключается в последовательном вызове двух методов объекта theApp: InitInstance и Run. В терминах сообщений можно сказать, WinMain посылает объекту theApp сообщение InitInstance, которое приводит в действие метод InitInstance.

Получив сообщение InitInstance, theApp создает внутренние объекты приложения. Процесс создания выглядит как последовательное порождение одних объектов другими. Набор объектов, порождаемых в начале этой цепочки, определен структурой MFC практически однозначно — это главная рамка, шаблон, документ, облик. Их роли в работе приложения будут обсуждаться позже.

Следующее сообщение, получаемое theApp, — Run — приводит в действие метод Run. Оно как бы говорит объекту: «Начинай работу, начинай процесс обработки сообщений из внешнего мира». Объект theApp циклически выбирает сообщения из очереди и инициирует обработку сообщений объектами приложения.

Некоторые объекты имеют графический образ на экране, с которым может взаимодействовать пользователь. Эти интерфейсные объекты обычно связаны с Windows-окном. Среди них особенно важны главная рамка и облик. Именно им объект прежде всего распределяет сообщения из очереди через механизм Windows-окон и функцию Dispatch.

Когда пользователь выбирает команду меню окна главной рамки, то возникают командные сообщения. Они отправляются сначала объектом theApp объекту главная рамка, а затем обходят по специальному маршруту целый ряд объектов, среди которых первыми являются документ и облик, информируя их о пришедшей от пользователя команде.

При работе приложения возникают и обычные вызовы одними объектами методов других объектов. В объектно-ориентированной терминологии такие вызовы могут называться сообщениями. В Visual C++ некоторым методам приписан именно этот статус (например, методу OnDraw).

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

Каркас приложения

Наследование — одна из фундаментальных идей объектно-ориентированного программирования. Именно этот механизм наследования позволяет программисту дополнять и переопределять поведение базового класса, не вторгаясь в библиотеку MFC, которая остается неизменной. Все изменения делаются в собственном производном классе. Именно в этом и заключается работа программиста.

Объекты, их которых состоит приложение, являются объектами классов, производных от классов библиотеки MFC. Разработка приложения состоит в том, что программист берет из библиотеки MFC классы CWinApp, CFrameWnd, CDocument, CView и т.д. и строит производные классы. Приложение создается как совокупность объектов этих производных классов. Каждый объект несет в себе как наследуемые черты, определяемые базовыми классами, так и новые черты, добавленные программистом. Наследуемые черты определяют общую схему поведения, свойственную таким приложениям. Новые же черты позволяют реализовать специфические особенности поведения приложения, необходимые для решения стоящей перед ним задачи.

При определении производного класса программист может:

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

Приложение, построенное на основе библиотеки MFC, — «айсберг», большая часть которого невидима, но является основой всего приложения. Часть приложения, лежащую в библиотеке MFC, — framework — называется каркасом приложения. Рассмотрим работу приложения как процесс взаимодействия между каркасом и частью приложения, разработанной программистом. Совершенно естественно, что в методах, определенных программистом, могут встречаться вызовы методов базового класса, что вполне можно рассматривать как вызов функции из библиотеки. Важнее, однако, что и метод производного класса, определенный программистом, может быть вызван из метода родительского класса. Другими словами, каркас и производный класс в этом смысле равноправны — их методы могут вызывать друг друга. Такое равноправие достигается благодаря виртуальным методам и полиморфизму, имеющимся в арсенале объектно-ориентированного программирования.

Если метод базового класса объявлен виртуальным и разработчик переопределил его в производном классе, это значит, что при вызове данного метода в некоторой полиморфной функции базового класса в момент исполнения будет вызван метод производного класса и, следовательно, каркас вызывает метод, определенный программистом. Точнее говоря, обращение к этому методу должно производиться через ссылку на производный объект либо через объект, являющийся формальным параметром и получающий при вызове в качестве своего значения объект производного класса. Когда вызывается виртуальный метод М1, переопределенный разработчиком, то согласно терминологии Visual C++, каркас посылает сообщение М1 объекту производного класса, а метод М1 этого объекта обрабатывает это сообщение. Если сообщение М1 послано объекту производного класса, а обработчик этого сообщения не задан программистом, объект наследует метод М1 ближайшего родительского класса, в котором определен этот метод. Если же обработчик такого сообщения создан программистом, он автоматически отменяет действия, предусмотренные родительским классом в отсутствие этого обработчика.

Каркас приложений

С Visual C++ тесно связано еще одно понятие — каркас приложений, которое близко и созвучно понятию каркаса приложения, но в отличие от него относится не к одному конкретному приложению, а к библиотеке, с помощью которой строятся многие приложения. Каркас приложений — это библиотека классов, из которых программист берет не только набор классов, играющих роль дополнительных типов данных, но и классы, служащие строительными блоками приложения на самом верхнем уровне. С этой точки зрения, каркас приложения является частью каркаса приложений, относящейся к данному приложению. Примеры каркасов приложений — библиотеки классов MFC и OWL.

Проект приложения

О принципах устройства приложения рассказывалось выше. Теперь рассмотрим, как оно создается с помощью Visual C++. Сначала разберем одно важное понятие — проект. До сих пор приложение рассматривалось, как только как совокупность объектов базовых и производных классов. Но для обеспечения работы приложения требуется нечто большее — наряду с описанием классов необходимо описание ресурсов, связанных с приложением, нужна справочная система и т.п. Термин «проект» как раз и используется, когда имеется в виду такой более общий взгляд на приложение.

В среде Visual C++ можно строить различные типы проектов. Такие проекты после их создания можно компилировать и запускать на исполнение. Фирма Microsoft разработала специальный инструментарий, облегчающий и ускоряющий создание проектов в среде Visual C++. Например, мастер MFC AppWizard (exe) позволяет создать проект Windows-приложения которое имеет однодокументный, многодокументный или диалоговый интерфейс и использует библиотеку MFC.

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

Использование средств разработки

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

Благодаря MFC AppWizard среда разработчика позволяет быстро создавать шаблоны новых приложений. При этом программисту не приходится писать ни одной строчки кода. Достаточно ответить на ряд вопросов, касающихся того, какое приложение требуется создать, и исходные тексты шаблона приложения вместе с файлами ресурсов готовы. Эти тексты можно оттранслировать и получить готовый загрузочный модуль приложения.

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

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

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

Типы мастеров проектов

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

Рассмотрим некоторые типы проектов, которые можно создавать при помощи различных средств (мастеров проектов) Microsoft Visual C++:

  • MFC AppWizard (exe) — при помощи мастера приложений можно создать проект Windows-приложения которое имеет однодокументный, многодокументный или диалоговый интерфейс. Однодокументное приложеие может предоставлять пользователю в любой момент времени работать только с одним файлом. Многодокументное приложение, напротив, может одновременно представлять несколько документов, каждый в собственном окне. Пользовательский интерфейс диалогового приложения представляет собой единственное диалоговое окно.
  • MFC AppWizard (dll) — этот мастер приложений позволяет создать структуру DLL, основанную на MFC. При помощи него можно определить характеристики будующей DLL.
  • AppWizard ATL COM — это средство позволяет создать элемент управления ActiveX или сервер автоматизации, используя новую библиотеку шаблонов ActiveX (ActiveX Template Library — ATL). Опции этого мастера дают возможность выбрать активный сервер (DLL) или исполняемый внешний сервер (exe-файл).
  • Custom AppWizard — при помощи этого средства можно создать пользовательские мастера AppWizard. Пользовательский мастер может базироваться на стандартных мастерах для приложений MFC или DLL, а также на существующих проектах или содержать только определеямые разработчиком шаги.
  • DevStudio Add-in Wizard — мастер дополнений позволяет создавать дополнения к Visual Studio. Библиотека DLL расширений может поддерживать панели инструментов и реагировать на события Visual Studio.
  • MFC ActiveX ControlWizard — мастер элементов управления реализует процесс создания проекта, содержащего один или несколько элементов управления ActiveX, основанных на элементах управления MFC.
  • Win32 Application — этот мастер позволяет создать проект обычного Window-приложения. Проект создается незаполненным, файлы с исходным кодом в него следует добавлять вручную.
  • Win32 Console Application — мастер создания проекта консольного приложения. Консольная приложение — это программа, которая выполняется из командной cтроки окна DOS или Windows и не имеет графического интерфейса (окон). Проект консольного приложения создается пустым, предполагая добавление файлов исходного текста в него вручную.
  • Win32 Dynamic-Link Library — создание пустого проекта динамически подключаемой библиотеки. Установки компилятора и компоновщика будут настроены на создание DLL. Исходные файлы следует добавлять вручную.
  • Win32 Static Library — это средство создает пустой проект, предназначенный для генерации статической (объектной) библиотеки. Файлы с исходным кодом в него следует добавлять вручную.

Преимущества мастеров проектов

Рассмотрим преимущества использования мастеров в процессе создания приложений. Прежде всего, нужно отметить, что создание проекта — это не только творчество, но и большой объем технической работы, требующей внимания и аккуратности.

Например, все Windows-приложения имеют достаточно общую структуру, и, следовательно, можно построить некоторые шаблонные заготовки, подходящие для того или иного типа проектов. Построению таких заготовок способствует то, что приложения, создаваемые на основе MFC, строятся из элементов фиксированных классов. Логическим развитием этой идеи было введение специальных классов и специальной архитектуры построения приложения, которая подходила бы широкому классу приложений. О такой архитектуре уже упоминалось, когда речь шла о библиотеке MFC, — это архитектура Document-View. Она является основной, но не единственной при построении проектов в среде Visual C++.

Суть этой архитектуры в том, что работу многих приложений можно рассматривать как обработку документов. При этом можно отделить сам документ, отвечающий за представление и хранение данных, от образа этого документа, видимого на экране и допускающего взаимодействие с пользователем, который просматривает и (или) редактирует документ. В соответствии с этой архитектурой библиотека MFC содержит два семейства классов, производных от базовых классов CDocument и CView.

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

Начальная заготовка — остов приложения — создается в диалоге с пользователем инструментальным средством AppWizard. В процессе диалога пользователь определяет тип и характеристики проекта, который он хочет построить. Определив, какие классы из MFC необходимы для этого проекта, AppWizard строит остовы всех нужных производных классов. Построенный AppWizard остов приложения содержит все необходимые файлы для создания стартового приложения, которое является законченным приложением и обладает разумными функциональными свойствами, общими для целого класса приложений. Естественно, никаких специфических для данного приложения свойств остов не содержит. Они появятся на следующем этапе, когда программист начнет работать с остовом, создавая из заготовки свое собственное приложение. Тем не менее стартовое приложение можно транслировать и запускать на исполнение.

Термин остов (приложения, класса, функции) применяется для заготовок, создаваемых инструментальными средствами AppWizard и ClassWizard. Нужно подчеркнуть — остов приложения и каркас приложения — разные понятия.

Создаваемый остов приложения составлен так, что в дальнейшей работе с проектом можно использовать другое инструментальное средство — ClassWizard (мастер классов).

Обзор возможностей ClassWizard

Средство ClassWizard предоставляет широкий спектр услуг. Он позволяет не только добавлять к существующему классу новые методы и данные.

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

Объекты, порожденные от класса CCmdTarget, могут обрабатывать сообщения Windows и команды, поступающие от меню, кнопок, акселераторов. Класс CCmdTarget и другие наследованные от него классы имеют таблицу сообщений (Message Map) — набор макрокоманд, позволяющий сопоставить сообщения Windows и команды метода класса.

Полученная заготовка класса полностью работоспособна. Ее можно дополнить по своему усмотрению новыми методами и данными. Эту работу можно выполнить вручную, но гораздо лучше и проще воспользоваться услугами ClassWizard. За счет использования ClassWizard процедура создания собственного класса значительно ускоряется и уменьшается вероятность совершить ошибку во время объявления методов.

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

ClassWizard не только позволяет добавить в класс новые методы, но и удалить их. ClassWizard самостоятельно удалит объявление метода из класса.

Включение в класс новых элементов данных. ClassWizard позволяет включать в класс не только новые методы, но и элементы данных, связанные с полями диалоговых панелей, форм просмотра и форм для просмотра записей баз данных и полей наборов записей. ClassWizard использует специальные процедуры, чтобы привязать созданные им элементы данных к класса к полям диалоговых панелей. Эти процедуры носят названия «обмен данными диалоговой панели» и «проверка данных диалоговой панели» (Dialog Data Exchange and Dialog Data Validation — DDX/DDV). Чтобы привязать поля из наборов записей к переменным, используется процедура обмена данными с полями записей (Record Field Exchange — RFX).

Процедуры DDX/DDV и RFX значительно упрощают программисту работу с диалоговыми панелями. Они позволяют связать поля диалоговых панелей и переменные. Когда пользователь редактирует поля диалоговых панелей, процедуры DDV проверяют введенные значения и блокируют ввод запрещенных значений. Затем процедуры DDX автоматически копируют содержимое полей диалоговых панелей в привязанные к ним элементы данных класса. И наоборот, когда приложение изменяет элементы данных класса, привязанные к полям диалоговой панели, процедуры DDX могут сразу отобразить новые значения полей на экране компьютера.

Имена, используемые в MFC

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

Названия всех классов и шаблонов классов библиотеки MFC начинаются с заглавной буквы C. При наследовании классов от классов MFC можно давать им любые имена. Рекомендуется начинать их названия с заглавной буквы C. Это сделает исходный текст приложения более ясным для понимания.

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

Библиотека MFC включает в себя, помимо классов, набор служебных функций. Названия этих функций начинаются с символов Afx, например AfxGetApp. Символы AFX являются сокращением от словосочетания Application FrameworkX, означающих основу приложения, его внутреннее устройство.

Символы AFX встречаются не только в названии функций MFC. Многие константы, макрокоманды и другие символы начинаются с этих символов. В общем случае AFX является признаком, по которому можно определить принадлежность того или иного объекта (функция, переменная, ключевое слово или символ) к библиотеке MFC.

Когда приложение разрабатывается средствами MFC AppWizard и ClassWizard, они размещают в исходном тексте приложения комментарии следующего вида:

	//{{AFX_ 	... 	//}}AFX_

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

В следующей таблице представлено краткое описание некоторых блоков //{{AFX_:

Блок Описание
//{{AFX_DATA
//}}AFX_DATA
Включает объявление элементов данных класса. Используется в описании классов диалоговых панелей.
//{{AFX_DATA_INIT
//}}AFX_DATA_INIT
Включает инициализацию элементов данных класса. Используется в файле реализации классов диалоговых панелей.
//{{AFX_DATA_MAP
//}}AFX_DATA_MAP
Включает макрокоманды DDX, предназначенные для связывания элементов данных класса и органов управления диалоговых панелей. Используется в файле реализации классов диалоговых панелей.
//{{AFX_MSG
//}}AFX_MSG
Включает описание методов, которые предназначены для обработки сообщений. Этот блок используется при описании класса.
//{{AFX_MSG_MAP
//}}AFX_MSG_MAP
Включает макрокоманды таблицы сообщений класса. Используются совместно с AFX_MSG.
//{{AFX_VIRTUAL
//}}AFX_VIRTUAL
Включает описание переопределенных виртуальных методов класса. Блок AFX_VIRTUAL используется при описании класса.

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

 //TODO:

Для того что бы перейти к Visual C++, целесообразно получить некоторое представление о просто языке C++, так как он является базовым. Что бы начать обучение вам нужно сначала поставить какую-нибудь версию Visual C++. Лучше, конечно, если у вас есть место на HDD, поставить Visual C++ версии 6. Но можно поставить версию 5 или 4. Все мои примеры написаны на шестой версии, но они должны работать и на младших версиях. Многие думают, что если они поставили Visual C++, то могут писать программы только под Windows, но это не правильно. Visual C++ позволяет писать программы и на простом C++, как бы под DOS. Ну вот, я думаю, и можно начинать.

  • Стартовая функция WinMain
  • Программа на Си для Windows, как и для любой другой платформы, должна
    обязательно содержать некоторую «стартовую» функцию, которой передается
    управление при запуске программы. Вообще говоря, имя такой «стартовой»
    функции может различаться в различных компиляторах, но исторически
    сложилось так (а, кроме того, имеются еще и стандарты ANSI и ISO,
    к которым, правда, производители коммерческих компиляторов типа Microsoft
    и Borland/Inprise относятся без особого трепета), что такой функцией является:

    int main()

    У этой функции может быть до трех параметров:

    int main(int argc, char *argv[], char *env[])
    • argc — количество параметров в командной строке (включая имя программы),
    • argv — массив строк-параметров (argv[0] — имя программы),
    • env — массив строк-переменных окружения.

    Многие компиляторы для Windows «понимают» такую стартовую функцию.
    Однако при этом они создают хотя и 32-битное, но консольное приложение.
    Пример 1 (example1.cpp):

    #include <stdio.h>
    int main() {
     printf("Hello, world!");
     getc(stdin);
     return 0;
    }

    Компилируем:

    bcc32 example1.cpp

    Запускаем:

    При использовании стандартных библиотек (stdio, stdlib и т. п.)
    вам не потребуется никаких лишних телодвижений по сравнению с обычными
    методами написания программ на Си. Если же ваша цель — 32-битное приложение
    с графическим интерфейсом, то черное консольное окошко будет вас раздражать.
    Пример (example2.cpp):

    #include <windows.h>
    int main() {
     MessageBox(NULL,"Hello, World!","Test",MB_OK);
     return 0;
    }

    В общем, чтобы получить нормальное приложение без каких-либо «довесков»
    типа консольного окошка, используется другая стартовая функция:

    int WINAPI WinMain (HINSTANCE hInst, HINSTANCE hpi, LPSTR cmdline, int ss)
    • hInst — дескриптор для данного экземпляра программы,
    • hpi — в Win32 не используется (всегда NULL),
    • cmdline — командная строка,
    • ss — код состояния главного окна.

    Пример (example3.cpp):

    #include <windows.h>
    int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int) {
     MessageBox(NULL,"Hello, World!","Test",MB_OK);
     return 0;
    }

    Кроме того, компилятору и компоновщику нужно сообщить о том, что
    вы делаете графическое приложение. Для bcc32 это опция -tW:

    bcc32 -tW example3.cpp

    Если же вы соскучитесь по черному консольному окошку, его можно
    в любой момент создать при помощи вызова Win32 API

    BOOL AllocConsole(void)

  • О типах, о функциях
  • Как известно, в Си есть лишь три базовых типа
    (char, int, float/double)
    и еще несколько их вариаций с модификаторами signed/unsigned, short/long.
    Однако фирме Microsoft зачем-то понадобилось описывать функции Win32 API
    с помощью переопределенных типов:

    typedef unsigned char BYTE;
    typedef unsigned short WORD;
    typedef unsigned int UINT;
    typedef int INT;
    typedef long BOOL;
    #define FALSE 0
    #define TRUE 1
    typedef long LONG;
    typedef unsigned long DWORD;
    typedef void *LPVOID;
    typedef char CHAR;
    typedef CHAR *LPSTR;
    typedef const CHAR *LPCSTR;

    Кроме перечисленных простых типов, практически ни один вызов Win32 API
    не обходится без «штучек» с «ручками» — переменных типа handle («ручка»),
    которые идентифицируют некоторый объект («штучку»). Такие «ручки» принято
    называть дескрипторами. Реально такая переменная представляет собой всего
    лишь указатель на некоторую системную структуру или
    индекс в некоторой системной таблице.

    typedef void *HANDLE;    /* абстрактный дескриптор (например, файла) */
    typedef void *HMODULE;   /* дескриптор модуля */
    typedef void *HINSTANCE; /* дескриптор экземпляра программы */
    typedef void *HKEY;      /* дескриптор ключа в реестре */
    typedef void *HGDIOBJ;   /* дескриптор графического примитива (перо, шрифт, кисть, палитра,...) */
    typedef void *HWND;      /* дескриптор окна */
    typedef void *HMENU;     /* дескриптор меню */
    typedef void *HICON;     /* дескриптор иконки */
    typedef void *HBITMAP;   /* дескриптор картинки */
    typedef void *HFONT;     /* дескриптор шрифта */

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

    В стандартных версиях Си для функций используются два варианта соглашения
    о передаче параметров: соглашение языка Си (параметры функции помещаются в стек в порядке
    обратном их описанию, очистку стека производит вызывающая процедура) и
    соглашение языка Паскаль (параметры функции помещаются в стек в (прямом) порядке их
    описания, очистку стека производит вызываемая процедура). Для этих соглашений
    использовались, соответственно, модификаторы cdecl и pascal.
    При описании функций Win32 API используется модификатор WINAPI, а для
    описания пользовательских функций обратного вызова — модификатор CALLBACK.
    Оба этих модификатора являются переопределением специального модификатора _stdcall,
    соответствующего соглашению о передаче параметров, использующегося исключительно
    в Win32 API, — Standard Calling Convention (параметры функции помещаются в стек
    в порядке обратном их описанию, очистку стека производит вызываемая процедура).

  • Окна
  • Окно — это прямоугольная область экрана, в котором приложение отображает информацию и
    получает реакцию от пользователя. Одновременно на экране может отображаться несколько
    окон, в том числе, окон других приложений, однако лишь одно из них может получать
    реакцию от пользователя — активное окно. Пользователь использует клавиатуру, мышь
    и прочие устройства ввода для взаимодействия с приложением, которому принадлежит активное окно.
    Каждое 32-битное приложение создает, по крайней мере, одно окно, называемое главным окном,
    которое обеспечивает пользователя основным интерфейсом взаимодействия с приложением.

    Окно приложения может содержать строку заголовка title bar (1),
    строку меню menu bar (2), системное меню system menu (3),
    кнопку сворачивания окна minimize box (4), кнопку разворачивания окна maximize box (5),
    рамку изменения размеров sizing border (6), клиентскую область client area (7),
    горизонтальную и вертикальную полосы прокрутки scroll bars (8):

    Меню, строка заголовка с системными кнопками, системное меню, рамка изменения размеров
    и полосы прокрутки относятся к области окна, называемой неклиентской областью (non-client area).
    С неклиентской областью Windows справляется сама, а вот за содержимое и обслуживание
    клиентской области отвечает приложение.

    Кроме главного окна, приложение может использовать еще и другие типы окон:
    управляющие элементы (controls), диалоговые окна (dialog boxes),
    окна-сообщения (message boxes). Управляющий элемент — окно, непосредственно
    обеспечивающее тот или иной способ ввода информации пользователем. К управляющим
    элементам относятся: кнопки, поля ввода, списки, полосы прокрутки и т.п.
    Управляющие элементы обычно не болтаются сами по себе, а проживают в каком-либо
    диалоговом окне.
    Диалоговое окно — это временное окно, напичканное управляющими элементами,
    обычно использующееся для получения дополнительной информации от пользователя.
    Диалоговые окна бывают модальные (modal) и немодальные (modeless).
    Модальное диалоговое окно требует, чтобы пользователь обязательно ввел обозначенную
    в окне информацию и закрыл окно прежде, чем приложение продолжит работу.
    Немодальное диалоговое окно позволяет пользователю, не закрывая диалогового окна,
    переключаться на другие окна этого приложения.
    Окно-сообщение — это диалоговое окно предопределенного системой формата,
    предназначенное для вывода небольшого текстового сообщения с одной или несколькими
    кнопками. Пример такого окна показан в Примере 3.

    В отличие от традиционного программирования на основе линейных алгоритмов,
    программы для Windows строятся по принципам событийно-управляемого программирования
    (event-driven programming) — стиля программирования, при котором поведение
    компонента системы определяется набором возможных внешних событий и ответных реакций
    компонента на них. Такими компонентами в Windows являются окна. С каждым окном
    в Windows связана определенная функция обработки событий. События для окон
    называются сообщениями. Сообщение относится к тому или иному типу,
    идентифицируемому определенным кодом (32-битным целым числом), и сопровождается
    парой 32-битных параметров (WPARAM и LPARAM),
    интерпретация которых зависит от типа сообщения. В заголовочном файле windows.h
    для кодов сообщений определены константы с интуитивно понятными именами:

    #define WM_CREATE   0x0001  /* сообщение о создании окна */
    #define WM_DESTROY  0x0002  /* сообщение об уничтожении окна */
    #define WM_SIZE     0x0005  /* сообщение об изменении размеров окна */
    #define WM_COMMAND  0x0111  /* сообщение от команды меню или управляющего элемента */

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

    Для стандартных управляющих элементов (библиотека Common Controls Library — COMCTL32.DLL)
    в Windows имеются предопределенные обработчики событий, которые при наступлении
    интересных событий сообщают всяческую полезную информацию окну, содержащему этот
    управляющий элемент. Стандартная библиотека Common Dialog Box Library
    (COMDLG32.DLL) содержит несколько готовых весьма полезных диалоговых окон с
    обработчиками: диалоги выбора файла, настроек печати, выбора шрифта, выбора цвета и др.
    Кроме того, любая среда разработки (VisualBasic, Delphi, VisualC++ и т.п.) навязывает
    разработчику дополнительный набор готовых управляющих элементов и диалогов —
    иногда достаточно удобных, иногда не очень.

  • Структура программы
  • Программа для Win32 обычно состоит из следующих блоков:

    #include <windows.h>
    
    int WINAPI WinMain(HINSTANCE hInst,HINSTANCE,LPSTR cmdline,int ss) {
    

    /* Блок инициализации: создание класса главного окна, создание главного окна, загрузка ресурсов и т.п. */

    /* Цикл обработки событий: */ MSG msg; while (GetMessage(&msg,(HWND)NULL,0,0)) { TranslateMessage(&msg); DispatchMessage(&msg); }

    return msg.wParam; } LRESULT CALLBACK MainWinProc(HWND hw,UINT msg,WPARAM wp,LPARAM lp) {

    /* Обработка сообщений главного окна */ switch (msg) { case WM_CREATE: /* ... */ return 0; case WM_COMMAND: /* ... */ return 0; case WM_DESTROY: /* ... */ PostQuitMessage(0); return 0; /* ... */ }

    return DefWindowProc(hw,msg,wp,lp); }

    Каждое окно принадлежит определенному классу окон. Окна одного класса
    имеют схожий вид, обслуживаются общей процедурой обработки событий,
    имеют одинаковые иконки и меню. Обычно каждое приложение создает
    для главного окна программы свой класс. Если приложению требуются
    дополнительные нестандартные окна, оно регистрирует другие классы.
    Стандартные диалоги и управляющие элементы принадлежат к предопределенным классам окон,
    для них не надо регистрировать новые классы. Чтобы определить новый класс окон,
    надо заполнить структуру WNDCLASS, содержащую следующие поля:

    • UINT style — стиль (поведение) класса окон,
    • WNDPROC lpfnWndProc — процедура обработки событий окна,
    • int cbClsExtra — размер дополнительной памяти в системной структуре класса для данных пользователя,
    • int cbWndExtra — размер дополнительной памяти в системной структуре окна для данных пользователя,
    • HINSTANCE hInstance — дескриптор модуля (экземпляра программы), в котором реализована процедура обработки,
    • HICON hIcon — дескриптор иконки окна,
    • HCURSOR hCursor — дескриптор курсора мыши для окна,
    • HBRUSH hbrBackground — дескриптор «кисточки» для закрашивания фона окна,
    • LPCSTR lpszMenuName — имя ресурса, содержащего меню окна,
    • LPCSTR lpszClassName — имя класса.

    Класс регистрируется при помощи функции:

    WORD WINAPI RegisterClass(const WNDCLASS *lpwc)

    При успешном завершении функция возвращает целочисленный код,
    соответствующий строке-имени класса в общесистемной таблице строк
    (такой код называется атомом). При ошибке возвращается 0.

    Для создания окна вызывается функция:

    HWND WINAPI CreateWindow(
      LPCSTR lpClassName,  /* имя класса */
      LPCSTR lpWindowName, /* имя окна (заголовок) */
      DWORD dwStyle,       /* стиль (поведение) окна */
      int x,               /* горизонтальная позиция окна на экране */
      int y,               /* вертикальная позиция окна на экране */
      int nWidth,          /* ширина окна */
      int nHeight,         /* высота окна */
      HWND hWndParent,     /* дескриптор родительского окна */
      HMENU hMenu,         /* дескриптор меню */
      HANDLE hInstance,    /* дескриптор экземпляра программы */
      LPVOID lpParam       /* указатель на какую-нибудь ерунду */
    )

    Вместо параметров x, y, nWindth, nHeight допустимо передавать
    константу CW_USEDEFAULT, позволяющую операционной системе задать эти числа
    по ее усмотрению.

    Интерпретация кода стиля определяется классом окна.
    Стиль определяет не только оформление окна, но и его поведение.
    Общие для всех классов константы стилей
    (при необходимости объединяются операцией побитовое ИЛИ):

    • WS_DISABLED — при создании окно заблокировано (не может получать реакцию от пользователя);
    • WS_VISIBLE — при создании окно сразу же отображается (не надо вызывать ShowWindow);
    • WS_CAPTION — у окна есть строка заголовка;
    • WS_SYSMENU — у окна есть системное меню;
    • WS_MAXIMIZEBOX — у окна есть кнопка разворачивания;
    • WS_MINIMIZEBOX — у окна есть кнопка сворачивания;
    • WS_SIZEBOX или WS_THICKFRAME — у окна есть рамка изменения размеров;
    • WS_BORDER — у окна есть рамка (не подразумевает изменение размеров);
    • WS_HSCROLL или WS_VSCROLL — у окна есть горизонтальная или вертикальная прокрутка;
    • WS_OVERLAPPED или WS_TILED — «перекрываемое» окно — обычное окно с рамкой и строкой заголовка;
    • WS_POPUP — «всплывающее» окно;
    • WS_OVERLAPPEDWINDOW — «перекрываемое» окно с системным меню, кнопками сворачивания/разворачивания,
      рамкой изменения размеров, короче, типичный стиль для главного окна приложения.

    Во время выполнения функции CreateWindow процедуре обработки событий
    окна посылается сообщение WM_CREATE. При успешном выполнении функции
    возвращается дескриптор созданного окна, при неудаче — NULL.

    После создания окна неплохо бы сделать его видимым (отобразить), если только
    оно не создано со стилем WS_VISIBLE:

    BOOL WINAPI ShowWindow(HWND hw, int ss)

    Второй параметр этой функции — код состояния отображения окна. В качестве этого кода
    можно взять значение четвертого параметра, с которым была запущена функция WinMain.
    Другие возможные значения этого параметра:

    • SW_SHOW — отобразить и активировать окно;
    • SW_HIDE — скрыть окно;
    • SW_MAXIMIZE — развернуть окно на весь экран;
    • SW_RESTORE — активировать окно и отобразить его в размерах по умолчанию;
    • SW_MINIMIZE — свернуть окно.

    Если перед вызовом этой функции окно было видимым, функция возвращает TRUE,
    если же окно было скрыто — FALSE.

    Если клиентская область главного окна приложения содержит объекты, прорисовываемые
    по сообщению WM_PAINT, имеет смысл прорисовать эти объекты сразу после
    отображения главного окна на экране. Функция UpdateWindow непосредственно
    вызывает процедуру обработки событий указанного окна с сообщением WM_PAINT
    (минуя очередь сообщений приложения):

    BOOL WINAPI UpdateWindow(HWND hw)

    Windows использует два способа доставки сообщений процедуре обработки событий окна:

    • непосредственный вызов процедуры обработки событий (внеочередные или
      неоткладываемые сообщенияnonqueued messages);
    • помещение сообщения в связанный с данным приложением буфер типа FIFO,
      называемый очередью сообщенийmessage queue
      (откладываемые сообщенияqueued messages).

    К внеочередным сообщениям относятся те сообщения, которые непосредственно
    влияют на окно, например, сообщение активации окна WM_ACTIVATE и т.п.
    Кроме того, вне очереди сообщений обрабатываются сообщения, сгенерированные
    различными вызовами Win32 API, такими как SetWindowPos,
    UpdateWindow, SendMessage,
    SendDlgItemMessage

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

    BOOL WINAPI GetMessage(
      MSG *lpmsg,          /* сюда попадает сообщение со всякими параметрами */
      HWND hw,             /* извлекать только сообщения для указанного окна (NULL - все) */
      UINT wMsgFilterMin,  /* фильтр сообщений (нам не надо - ставим 0) */
      UINT wMsgFilterMax   /* фильтр сообщений (нам не надо - ставим 0) */
    )

    Эта функция возвращает FALSE, если получено сообщение WM_QUIT,
    и TRUE в противном случае. Очевидно, что условием продолжения
    цикла обработки событий является результат этой функции. Если приложение хочет
    завершить свою работу, оно посылает само себе сообщение WM_QUIT
    при помощи функции

    void WINAPI PostQuitMessage(int nExitCode)

    Ее параметр — статус выхода приложения. Обычно эта функция вызывается в ответ на
    сообщение об уничтожении окна WM_DESTROY.

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

    BOOL WINAPI TranslateMessage(const MSG *lpmsg)
    LONG WINAPI DispatchMessage(const MSG *lpmsg)

    Результат возврата соответствует значению, которое вернула процедура обработки событий
    (обычно никому не нужен).

    Процедура обработки сообщений окна должна быть объявлена по следующему прототипу:

    LRESULT CALLBACK WindowProc(HWND hw,UINT msg,WPARAM wp,LPARAM lp)

    Значения параметров: hw — дескриптор окна, которому предназначено сообщение,
    msg — код сообщения, wp и lp — 32-битные параметры
    сообщения, интерпретация которых зависит от кода сообщения. Зачастую старший/младший
    байт или старшее/младшее слово параметров сообщения несут независимый смысл, тогда
    удобно использовать определенные в windows.h макросы:

    #define LOBYTE(w)   ((BYTE) (w))
    #define HIBYTE(w)   ((BYTE) (((WORD) (w) >> 8) & 0xFF))
    #define LOWORD(l)   ((WORD) (l))
    #define HIWORD(l)   ((WORD) (((DWORD) (l) >> 16) & 0xFFFF))

    Например, сообщение WM_COMMAND посылается окну в трех случаях:

    1. пользователь выбрал какую-либо команду меню;
    2. пользователь нажал «горячую» клавишу (accelerator);
    3. в дочернем окне произошло определенное событие.

    При этом параметры сообщения интерпретируются следующим образом.
    Старшее слово параметра WPARAM содержит: 0 в первом случае, 1 во втором случае
    и код события в третьем случае. Младшее слово WPARAM содержит
    целочисленный идентификатор пункта меню, «горячей» клавиши или дочернего управляющего
    элемента. Параметр LPARAM в первых двух случаях содержит NULL,
    а в третьем случае — дескриптор окна управляющего элемента.

    Процедура обработки событий должна вернуть определенное 32-битное значение,
    интерпретация которого также зависит от типа сообщения. В большинстве случаев,
    если сообщение успешно обработано, процедура возвращает значение 0.

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

    LRESULT WINAPI DefWindowProc(HWND hw, UINT msg, WPARAM wp, LPARAM lp)

    Все описанное в данном параграфе суммируется в примере 4 (example4.cpp):

    #include <windows.h>
    
    LRESULT CALLBACK MainWinProc(HWND,UINT,WPARAM,LPARAM);
    #define ID_MYBUTTON 1    /* идентификатор для кнопочки внутри главного окна */
    
    int WINAPI WinMain(HINSTANCE hInst,HINSTANCE,LPSTR,int ss) {
     /* создаем и регистрируем класс главного окна */
     WNDCLASS wc;
     wc.style=0;
     wc.lpfnWndProc=MainWinProc;
     wc.cbClsExtra=wc.cbWndExtra=0;
     wc.hInstance=hInst;
     wc.hIcon=NULL;
     wc.hCursor=NULL;
     wc.hbrBackground=(HBRUSH)(COLOR_WINDOW+1);
     wc.lpszMenuName=NULL;
     wc.lpszClassName="Example 4 MainWnd Class";
     if (!RegisterClass(&wc)) return FALSE;
    
     /* создаем главное окно и отображаем его */
     HWND hMainWnd=CreateWindow("Example 4 MainWnd Class","EXAMPLE 4",WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,hInst,NULL);
     if (!hMainWnd) return FALSE;
     ShowWindow(hMainWnd,ss);
     UpdateWindow(hMainWnd);
    
     MSG msg; /* цикл обработки событий */
     while (GetMessage(&msg,NULL,0,0)) {
      TranslateMessage(&msg); 
      DispatchMessage(&msg); 
     } 
     return msg.wParam; 
    }
    
    /* процедура обработки сообщений для главного окна */
    LRESULT CALLBACK MainWinProc(HWND hw,UINT msg,WPARAM wp,LPARAM lp) {
     switch (msg) {
      case WM_CREATE:
       /* при создании окна внедряем в него кнопочку */
       CreateWindow("button","My button",WS_CHILD|BS_PUSHBUTTON|WS_VISIBLE,
        5,5,100,20,hw,(HMENU)ID_MYBUTTON,NULL,NULL);
       /* стиль WS_CHILD означает, что это дочернее окно и для него
        вместо дескриптора меню будет передан целочисленный идентификатор,
        который будет использоваться дочерним окном для оповещения 
        родительского окна через WM_COMMAND */
       return 0;
      case WM_COMMAND:
       /* нажата наша кнопочка? */
       if ((HIWORD(wp)==0) && (LOWORD(wp)==ID_MYBUTTON)) 
        MessageBox(hw,"You pressed my button","MessageBox",MB_OK|MB_ICONWARNING);
       return 0;
      case WM_DESTROY:
       /* пользователь закрыл окно, программа может завершаться */
       PostQuitMessage(0);
       return 0;
     }
     return DefWindowProc(hw,msg,wp,lp);
    }

    Приведенный пример создает окно с кнопкой «My button», при нажатии
    на которую вылезает окно-сообщение:

  • Ресурсы
  • Ресурсы — это бинарные данные, добавляемые в исполняемый файл
    при компоновке программы. К стандартным ресурсам относятся: иконки,
    курсоры, меню, диалоги, растровые изображения (BMP), векторные изображения (EMF),
    шрифты, таблицы горячих клавиш, таблицы строк, информация о версии программы или модуля.
    В процессе разработки программы ресурсы описывают в отдельном текстовом файле —
    файле описания ресурсов (расширение .rc), — а затем при помощи компилятора
    ресурсов переводят в бинарный вид и добавляют в исполняемый файл на этапе компоновки
    исполняемого файла. Использование ресурсов значительно облегчает работу программиста
    по визуализации графических примитивов интерфейса программы.

    Файл описания ресурсов состоит из операторов, объединяемых в блоки.
    Один оператор занимает одну строку файла. Допускается использовать
    комментарии, определяемые так же, как в программе на языке Си.
    Файл описания ресурсов перед компиляцией так же обрабатывается препроцессором,
    поэтому в нем можно использовать директивы препроцессора (#include,
    #define, …) и макроопределения. В сложных «блочных»
    описаниях ресурсов вместо ключевых слов BEGIN и END
    можно использовать { и }, соответственно.

    Иконки, картинки и курсоры мыши можно описать двумя способами
    (квадратные скобки не являются частью синтаксиса оператора и означают
    необязательный элемент):

    nameID RESOURCETYPE [load-option] [mem-option] filename

    Здесь nameID — численный или строковой идентификатор;
    RESOURCETYPE — ключевое слово, обозначающее тип ресурса:
    ICON, BITMAP или
    CURSOR;
    load-option и mem-option — всякие неинтересные
    в данный момент опции, которые можно спокойно пропустить;
    filename — имя файла, содержащее соответствующий ресурс.

    Примеры:

    disk1   BITMAP "disk.bmp"
    12      ICON   "myicon.ico"

    Эти ресурсы можно внедрить в виде шестнадцатеричных кодов
    непосредственно в файл ресурсов:

    nameID RESOURCETYPE
    BEGIN
     hex data
    END

    Пример:

    FltBmp BITMAP
    {
     '42 4D A2 00 00 00 00 00 00 00 3E 00 00 00 28 00'
     '00 00 19 00 00 00 19 00 00 00 01 00 01 00 00 00'
     '00 00 64 00 00 00 00 00 00 00 00 00 00 00 00 00'
     '00 00 00 00 00 00 00 00 00 00 FF FF FF 00 FF FF'
     'FF 80 FF FF FF 80 FF FF FF 80 FF FF FF 80 FF FF'
     'FF 80 FF FF FF 80 FF FF FF 80 C0 FF 81 80 FE FF'
     'BF 80 FE FF BF 80 FE FF BF 80 FE FF BF 80 FE FF'
     'BF 80 FE FF BF 80 FE FF BF 80 FE FF BF 80 FE FF'
     'BF 80 FE FF BF 80 FE 00 3F 80 FF FF FF 80 FF FF'
     'FF 80 FF FF FF 80 FF FF FF 80 FF FF FF 80 FF FF'
     'FF 80'
    }

    Следует отметить, что первая иконка в ресурсах
    будет использоваться «Проводником» как иконка
    исполняемого файла.

    Меню описывается следующим образом:

    nameID MENU [load-option] [mem-option]
    BEGIN 
        item-definitions
        ...
    END

    Здесь item-definitions — один из трех операторов:

    MENUITEM text, result [, optionlist]
            /* обычный пункт меню */
    MENUITEM SEPARATOR
            /* строка-сепаратор */
    POPUP text [, optionlist]
    BEGIN   /* подменю */
        item-definitions
        ...
    END

    Параметры операторов имеют следующий смысл:
    text — текст пункта меню или подменю
    (может содержать комбинации t — табуляция,
    a — выравнивание по правому краю, &
    следующий символ подчеркивается, обозначает «горячую» клавишу для
    указанного пункта меню);
    result — целочисленный идентификатор пункта меню,
    посылаемый окну-владельцу через сообщение WM_COMMAND
    при выборе этого пункта меню;
    optionlist — необязательный список опций, разделенных
    запятой или пробелом:

    • CHECKED — рядом с пунктом меню отображается галочка,
    • GRAYED — пункт меню неактивен (не может быть выбран)
      и отображается серым цветом и др.

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

    HICON WINAPI LoadIcon(HINSTANCE hInst, LPCSTR lpIconName)
    HBITMAP WINAPI LoadBitmap(HINSTANCE hInst, LPCSTR lpBitmapName)
    HCURSOR WINAPI LoadCursor(HINSTANCE hInst, LPCSTR lpCursorName)
    HMENU WINAPI LoadMenu(HINSTANCE hInst, LPCSTR lpMenuName)

    Первый параметр этих функций — дескриптор экземпляра программы,
    второй — идентификатор соответствующего ресурса. Если ресурс идентифицируется
    не именем, а числом, то следует использовать макрос, объявленный в windows.h:

    #define MAKEINTRESOURCE(i)  (LPSTR) ((DWORD) ((WORD) (i)))

    Например:

    HMENU hMainMenu=LoadMenu(hInst,MAKEINTRESOURCE(10));

    Для закрепления полученных сведений, давайте добавим к примеру 4
    какую-нибудь иконку и такое меню:

    Для этого создаем файл ресурсов (example4a.rc):

    Ex4_Icon ICON "myicon.ico"
    
    Ex4_Menu MENU
    {
     POPUP "&File"
     {
      MENUITEM "&Open...tCtrl-O", 2
      MENUITEM "&Save", 3
      MENUITEM "Save &As...", 4
      MENUITEM SEPARATOR
      MENUITEM "&Hex view", 5, CHECKED GRAYED
      MENUITEM "&ExittAlt-F4", 6
     }
     POPUP "&Edit"
     {
      MENUITEM "&Copy", 7
      MENUITEM "&Paste", 8
      POPUP "Popup"
      {
       MENUITEM "1", 9
       MENUITEM "2", 10
       MENUITEM "3", 11
      }
      MENUITEM SEPARATOR
      MENUITEM "Search", 12
     }
     POPUP "&Help"
     {
      MENUITEM "&About...tF1", 13
     }
    }

    Для перевода файла описания ресурсов в бинарный вид используется
    компилятор ресурсов Borland Resource Compiler:

    brcc32 example4a.rc

    В результате получается файл example4a.res, который потребуется в процессе
    компоновки.

    В примере 4 надо изменить строки

     wc.hIcon=NULL;
     wc.lpszMenuName=NULL;

    на

     wc.hIcon=LoadIcon(hInst,"Ex4_Icon");
     wc.lpszMenuName="Ex4_Menu";

    Чтобы программа не была такой скучной, изменим обработчик сообщения WM_COMMAND:

      case WM_COMMAND:
       if (HIWORD(wp)==0) {
        char buf[256];
        switch (LOWORD(wp)) {
         case 6:  /* команда меню Exit */
          PostQuitMessage(0);
         default: /* все остальные команды */
          wsprintf(buf,"Command code: %d",LOWORD(wp));
          MessageBox(hw,buf,"MessageBox",MB_OK|MB_ICONINFORMATION);
        }
       }
       return 0;

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

    Обратите внимание: среди команд меню не используется код 1,
    который отведен кнопке «My button». Это типичная практика назначать
    всем дочерним элементам окна и командам меню разные численные идентификаторы,
    что облегчает обработку сообщения WM_COMMAND.

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

    bcc32 -c -tW example4a.cpp

    В результате получаем объектный файл example4a.obj.

    Чтобы собрать все части программы вместе, придется запускать
    компоновщик вручную (ilink32 или tlink32).
    В командной строке компоновщика указываются следующие параметры:

    ilink32 [options] objfiles,exefile,mapfile,libfiles,deffile,resfiles
    • options — о допустимых опциях можно узнать, запустив компоновщик без параметров.
      Нам потребуются:

      • -aa — тип приложения «графическое для Win32»
        (другие варианты: -ap — консольное приложение, -ad — драйвер);
      • -Tpe — формат выходного файла «.EXE» (другой вариант: -Tpd — «.DLL»);
      • -L путь — путь для поиска библиотек и объектных файлов
        (обычно: -Lc:bcc55lib).
    • objfiles — список объектных файлов, из которых составляется программа,
      разделенных пробелом или знаком «+». Этот список должен начинаться с борландовского
      инициализационного объектного файла: c0w32.obj — для графического приложения под
      Win32 или c0x32.obj — для консольного приложения.
    • exefile — имя исполняемого файла, который получится в результате компоновки.
    • mapfile — имя файла, который после компиляции будет содержать
      карту сегментов вашей программы (оно вам надо? если нет, здесь делаем «пусто»,
      а в опциях указываем -x, чтобы компоновщик не замусоривал рабочий каталог этой фигней).
    • libfiles — список библиотек, в которых надо искать не
      определенные в программе функции, разделенных пробелом или знаком «+».
      Как минимум, надо указать import32.lib, которая содержит код подключения
      к стандартным библиотекам Win32 API: kernel32.dll, user32.dll, gdi32.dll,
      advapi32.dll и др. Если вы используете какие-либо функции стандартных
      библиотек языка Си (stdlib, stdio, …), надо указать еще cw32.lib.
    • deffile — файл параметров модуля (module definition file).
      Это текстовый файл, в котором определяются различные настройки компилируемой
      программы типа: размеры сегментов стека, кода, данных, «заглушка»
      (что будет происходить при попытке запуска программы в DOS) и проч.
      Если не указывать этот файл, компоновщик выберет вполне приличные
      для большинства случаев параметры по умолчанию.
    • resfiles — файлы ресурсов (разделяются пробелом или знаком «+»).

    Компоновщик ilink32 умеет работать в пошаговом (инкрементирующем)
    режиме incremental linking, при этом он создает несколько файлов состояний
    (*.IL?). При последующих попытках компиляции он использует их, так что
    процесс компиляции занимает меньше времени. Чтобы отключить пошаговую
    компиляцию и не замусоривать рабочий каталог этими файлами, следует
    указать опцию -Gn. Например, если при отключенной пошаговой компиляции
    программа компилируется 8 секунд, то первая компиляция в пошаговом режиме
    займет 25 секунд, а все последующие — не более 2 секунд.

    Итак, компонуем модифицированный пример 4:

    ilink32 -aa -Tpe -Lc:bcc55lib c0w32.obj+example4a.obj,example4a.exe,,import32.lib+cw32.lib,,example4a.res

    Если все сделано правильно, первое, что становится сразу заметным —
    у исполняемого файла появилась иконка:

    Эта же иконка отображается в строке заголовка главного окна программы. Под
    строкой заголовка отображается созданное нами меню. При выборе любой команды
    меню появляется окно-сообщение с кодом команды. При выборе команды «Exit»
    программа завершается.

    Если ваш проект состоит из множества файлов, то компилировать и
    компоновать их вручную становится затруднительно. В таких случаях
    используются сценарии компиляции, которые обрабатываются программой make.
    Сценарий компиляции — текстовый файл с именем Makefile, описывающий
    зависимости между различными файлами проекта, следующего формата:

    правило: зависимости
    	команды для выполнения

    Сценарий компиляции должен содержать как минимум одно правило.
    Строка с командами обязательно должна начинаться с отступа табуляцией.
    В качестве имени правила обычно выступает имя файла, который получится
    в результате выполнения команд в теле правила.
    Зависимости — необязательный список имен файлов, разделенных пробелами,
    от которых зависит данное правило. Если при вызове make окажется,
    что хотя бы один файл из этого списка новее, чем файл-результат правила,
    то выполняются все команды из этого правила. В качестве зависимостей
    могут указываться имена файлов-названия других правил. Тогда make
    будет выполнять рекурсивную проверку зависимостей. Make не выполняет
    команды из правила, если все файлы-зависимости старее файла-результата.

    Пример:

    example4a.exe: example4a.rc example4a.cpp myicon.ico
    	brcc32 example4a.rc
    	bcc32 -c -tW example4a.cpp
    	ilink32 -Gn -x -aa -Tpe -Lc:bcc55lib c0w32.obj+example4a.obj,example4a.exe,,import32.lib+cw32.lib,,example4a.res

    Если не указывать в качестве файлов-зависимостей example4a.rc и example4a.cpp,
    то make не станет ничего делать, когда файл example4a.exe уже существует.
    Тем не менее, приведенный пример — не совсем удачный сценарий компиляции.
    Если мы изменим только файл ресурсов, make все равно будет перекомпилировать
    исходный текст. Если мы изменим только исходный текст, make будет
    перекомпилировать еще и ресурсы. С учетом этого замечания, более удачным
    будет следующий сценарий:

    example4a.exe: example4a.obj example4a.res
    	ilink32 -Gn -x -aa -Tpe -Lc:bcc55lib c0w32.obj+example4a.obj,example4a.exe,,import32.lib+cw32.lib,,example4a.res
    
    example4a.obj: example4a.cpp
    	bcc32 -c -tW example4a.cpp
    
    example4a.res: example4a.rc myicon.ico
    	brcc32 example4a.rc

    Если в командной строке make не указано иное, то make пытается выполнить
    первое правило из сценария. Именно поэтому первым правилом стоит example4a.exe
    — результат, который мы хотим получить после компиляции всего проекта.

    Если написать подходящий сценарий компиляции, то для компиляции
    вашего проекта придется набирать лишь команду:

    make

  • Диалоговые окна
  • Большинство приложений использует диалоговые окна для запроса у пользователя
    дополнительной информации для выполнения каких-либо команд. Например,
    команда открытия файла требует указания имени файла, так что приложение
    создает диалоговое окно, чтобы запросить у пользователя имя файла.
    Пока пользователь не укажет имя файла, команда не будет выполнена.
    После этого программа уничтожает это диалоговое окно. В этом случае
    используется модальное диалоговое окно. Другой пример: текстовый
    редактор может использовать немодальное диалоговое окно, для
    команды поиска. Пока редактор ищет введенную фразу, диалоговое окно
    остается на экране. Более того, пользователь может вернуться к редактированию
    текста, не закрывая диалог поиска. Либо пользователь может ввести
    другую фразу для поиска. Такое диалоговое окно остается открытым,
    пока приложение не завершится или пользователь непосредственно
    не выберет команду закрытия этого диалога.

    Чтобы создать диалоговое окно, приложение должно предоставить
    системе шаблон диалога, описывающий содержание и стиль диалога,
    и диалоговую процедуру. Диалоговая процедура выполняет примерно
    такие же задачи, что и процедура обработки событий окна.
    Диалоговые окна принадлежат к предопределенному классу окон.
    Windows использует этот класс и соответствующую процедуру
    обработки событий для модальных и немодальных диалогов.
    Эта процедура обрабатывает одни сообщения самостоятельно,
    а другие передает на обработку диалоговой процедуре приложения.
    У приложения нет непосредственного доступа к этому предопределенному
    классу и соответствующей ему процедуре обработки событий.
    Для изменения стиля и поведения диалога программа должна использовать
    шаблон диалогового окна и диалоговую процедуру.

    Для создания модального диалога используется функция DialogBox,
    а для создания немодального диалога — CreateDialog:

    int WINAPI DialogBox(HANDLE hInst, LPCSTR template, HWND parent, DLGPROC DlgFunc)
    HWND WINAPI CreateDialog(HANDLE hInst, LPCSTR template, HWND parent, DLGPROC DlgFunc)

    Параметры: hInst — дескриптор экземпляра программы
    (модуля, в котором находится шаблон); template — имя ресурса,
    описывающего диалог; parent — дескриптор родительского окна;
    DlgFunc — диалоговая функция следующего формата:

    BOOL CALLBACK DlgFunc(HWND hw, UINT msg, WPARAM wp, LPARAM lp) 

    Параметры диалоговой функции такие же, как у обычной функции обработки
    событий. Отличие этой функции — она вызывается из предопределенной
    функции обработки событий для диалоговых окон. Она должна вернуть
    значение TRUE, если обработала переданное ей сообщение,
    или FALSE в противном случае. Она ни в коем случае
    не должна сама вызывать DefWindowProc.

    При создании диалогового окна диалоговая процедура получает
    сообщение WM_INITDIALOG. Если в ответ на это сообщение
    процедура возвращает FALSE, диалог не будет создан:
    функция DialogBox вернет значение -1,
    а CreateDialogNULL.

    Модальное диалоговое окно блокирует указанное в качестве родительского
    окно и появляется поверх него (вне зависимости от стиля WS_VISIBLE).
    Приложение закрывает модальное диалоговое окно при помощи функции

    BOOL WINAPI EndDialog(HWND hw, int result)

    Приложение должно вызвать эту функцию из диалоговой процедуры в ответ
    на сообщение от кнопок «OK», «Cancel» или команды «Close» из системного
    меню диалога. Параметр result передается программе как
    результат возврата из функции DialogBox.

    Немодальное диалоговое окно появляется поверх указанного в качестве
    родительского окна, но не блокирует его. Диалоговое окно остается
    поверх родительского окна, даже если оно неактивно. Программа сама
    отвечает за отображение/сокрытие окна (с помощью стиля WS_VISIBLE
    и функции ShowWindow). Сообщения для немодального
    диалогового окна оказываются в основной очереди сообщений программы.
    Чтобы эти сообщения были корректно обработаны, следует включить в цикл
    обработки сообщений вызов функции:

    BOOL WINAPI IsDialogMessage(HWND hwDlg, MSG *lpMsg)

    Если эта функция вернула TRUE, то сообщение обработано
    и его не следует передавать функциям TranslateMessage
    и DispatchMessage.

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

    BOOL WINAPI DestroyWindow(HWND hw)

    Шаблон диалогового окна в ресурсах задается следующим образом:

    nameID DIALOG [load-option] [mem-option] x, y, width, height
        [property-statements]
    BEGIN 
        control-statements
        ...
    END

    В начале блока описания диалога задается: nameID
    целочисленный или строковой идентификатор ресурса, x, y
    координаты диалога на экране (или относительно родительского окна),
    width, height — размер диалога.
    Координаты и размеры диалога и всех элементов внутри
    него задаются в диалоговых единицах (dialog units).
    Одна горизонтальная диалоговая единица соответствует 1/4 средней ширины
    символа в системном шрифте. Одна вертикальная диалоговая единица
    соответствует 1/8 средней высоты символа в системном шрифте.

    После заголовка блока идет ряд необязательных операторов-параметров диалога
    (property-statements) в любом порядке:

    STYLE style               /* стиль диалога */
    CAPTION captiontext       /* заголовок диалога */
    FONT pointsize, typeface  /* шрифт диалога (размер, название) */
    MENU menuname             /* меню диалога */

    В качестве стиля диалога можно применять все перечисленные для обычных окон
    стили. Обычно выбирают: WS_POPUP, WS_SYSMENU, WS_CAPTION,
    а также WS_BORDER для немодального диалога и DS_MODALFRAME
    для модального. Кроме того, можно использовать DS_SETFOREGROUND,
    чтобы при отображении диалога перевести его на передний план, даже если
    его родительское окно неактивно.

    В теле шаблона (control-statements) перечисляются
    составляющие его управляющие элементы. Один из возможных вариантов оператора:

    CONTROL text, id, class, style, x, y, width, height

    Здесь text — текст управляющего элемента (заголовок),
    id — целочисленный идентификатор элемента 0…65535
    (внутри одного диалога идентификаторы всех элементов должны различаться),
    class — имя класса, к которому принадлежит управляющий элемент,
    style — стиль управляющего элемента,
    x, y, width, height
    положение и размер диалогового элемента относительно клиентской
    области диалога в диалоговых единицах.

    Вот пример диалога, содержащего простое статическое текстовое поле и кнопку «OK»:

    Ex4_Dlg DIALOG 50,50,90,40
     STYLE WS_POPUP|WS_CAPTION|DS_MODALFRAME
     CAPTION "MyDlg"
     FONT 10, "Arial"
    {
     CONTROL "", 1, "STATIC", SS_LEFT, 5, 5, 80, 10
     CONTROL "OK", 2, "BUTTON", BS_DEFPUSHBUTTON, 5, 20, 80, 12
    }

    Добавим этот диалог к ресурсам примера 4.
    В текст программы добавим две глобальных переменных:

    char buf[256]=""; /* строка для текстового поля в диалоге */
    HINSTANCE h;      /* дескриптор экземпляра программы */

    Присвоим переменной h значение дескриптора экземпляра программы в самом начале
    функции WinMain. Это значение нам потребуется для вызова функции
    DialogBox.

    Изменим обработчик сообщения WM_COMMAND следующим образом:

      case WM_COMMAND:
        switch (LOWORD(wp)) {
         case 6:  /* команда меню Exit */
          PostQuitMessage(0);
         default: /* все остальные команды */
          wsprintf(buf,"Command code: %d",LOWORD(wp));
          DialogBox(h,"Ex4_Dlg",hw,DlgProc);
        }
        return 0;

    Теперь в текст программы необходимо добавить диалоговую процедуру:

    BOOL CALLBACK DlgProc(HWND hw, UINT msg, WPARAM wp, LPARAM lp) {
     switch (msg) {
      case WM_INITDIALOG: /* сообщение о создании диалога */
       SetDlgItemText(hw,1,buf);
       return TRUE;
      case WM_COMMAND:    /* сообщение от управляющих элементов */
       if (LOWORD(wp)==2) EndDialog(hw,0);
     }
     return FALSE;
    }

    При создании диалога вызывается процедура SetDlgItemText,
    меняющая содержание текстового поля в диалоге (элемент с id=1).
    Для уничтожения диалога используется кнопка «OK», генерирующая
    сообщение WM_COMMAND с id=2.

    Функция DlgProc должна быть определена или описана
    до ссылки на нее в вызове DialogBox.

  • Управляющие элементы
  • Управляющие элементы, как и другие окна, принадлежат тому или
    иному классу окон. Windows предоставляет несколько предопределенных
    классов управляющих элементов. Программа может создавать управляющие
    элементы поштучно при помощи функции CreateWindow
    или оптом, загружая их вместе с шаблоном диалога из своих ресурсов.
    Управляющие элементы — это всегда дочерние окна. Управляющие элементы
    при возникновении некоторых событий, связанных с реакцией пользователя,
    посылают своему родительскому окну сообщения-оповещения
    (notification messages) WM_COMMAND или WM_NOTIFY.

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

    BOOL WINAPI EnableWindow(HWND hw,BOOL bEnable)

    В качестве второго параметра передается флаг TRUE (разблокировать)
    или FALSE (блокировать). Функция возвращает значение TRUE,
    если перед ее вызовом окно было заблокировано. Узнать текущий статус
    блокирования окна можно при помощи функции:

    BOOL WINAPI IsWindowEnabled(HWND hw),

    которая возвращает значение TRUE, если окно разблокировано.

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

    LRESULT WINAPI SendMessage(HWND hw, UINT msg, WPARAM wp, LPARAM lp)

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

    HWND WINAPI GetDlgItem(HWND hDlg, int idDlgItem)

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

    LRESULT WINAPI SendDlgItemMessage(
      HWND hwndDlg,	 /* дескриптор родительского диалога */
      int idControl, /* идентификатор управляющего элемента */
      UINT msg,      /* код сообщения */
      WPARAM wp,     /* параметр сообщения */
      LPARAM lp      /* параметр сообщения */
    )

    Для управляющих элементов внутри диалогов специальный смысл имеют
    стили WS_TABSTOP и WS_GROUP. Если в диалоге
    имеются управляющие элементы со стилем WS_TABSTOP, то при нажатии
    пользователем на клавишу [Tab] (или [Shift]+[Tab]), текущий активный элемент
    диалога будет терять фокус и передавать его следующему за ним (или предыдущему)
    ближайшему элементу со стилем WS_TABSTOP. С помощью стиля
    WS_GROUP элементы диалога можно объединять в группы. Группа
    элементов начинается с элемента со стилем WS_GROUP и заканчивается
    элементом, после которого идет элемент со стилем WS_GROUP, или
    последним элементом в диалоге. Внутри группы только первый элемент
    должен иметь стиль WS_GROUP. Windows допускает перемещение
    внутри группы при помощи клавиш-стрелок.

    Классы предопределенных управляющих элементов:

    «STATIC»
    Статический управляющий элемент представляет собой
    текстовую метку или прямоугольник. Не предоставляет пользователю
    ни средств ввода, ни вывода. Примеры:

    Соответствующее описание ресурсов:

     CONTROL "",-1, "STATIC", SS_BLACKFRAME,  5, 40, 20, 10
     CONTROL "",-1, "STATIC", SS_GRAYFRAME,  30, 40, 20, 10
     CONTROL "",-1, "STATIC", SS_WHITEFRAME, 55, 40, 20, 10
     CONTROL "",-1, "STATIC", SS_BLACKRECT,  80, 40, 20, 10
     CONTROL "",-1, "STATIC", SS_GRAYRECT,  105, 40, 20, 10
     CONTROL "",-1, "STATIC", SS_WHITERECT, 130, 40, 20, 10
     CONTROL "",-1, "STATIC", SS_ETCHEDFRAME,155, 40, 20, 10
    
     /* Для статиков-иконок или картинок текстовое поле определяет имя ресурса */
     CONTROL "Ex4_Bmp",-1, "STATIC", SS_BITMAP, 5, 55, -1, -1
     CONTROL "Ex4_Icon",-1, "STATIC", SS_ICON, 65, 55, -1, -1
    
     CONTROL "text",-1, "STATIC", SS_LEFT,    105, 55, 20, 10
     CONTROL "text",-1, "STATIC", SS_CENTER,  130, 55, 20, 10
     CONTROL "text",-1, "STATIC", SS_RIGHT,   155, 55, 20, 10
    
     /* По умолчанию SS_LEFT, SS_RIGHT, SS_CENTER делают перенос по словам */
     CONTROL "This is long text example",-1, "STATIC", SS_SIMPLE|SS_SUNKEN, 65, 70, 55, 15
     CONTROL "This is long text example",-1, "STATIC", SS_LEFT|SS_SUNKEN,  125, 70, 50, 15

    Для текстовых статиков со стилями SS_LEFT, SS_RIGHT
    или SS_CENTER существуют более простые операторы объявления ресурсов:

     LTEXT "text",-1, 105, 55, 20, 10
     CTEXT "text",-1, 130, 55, 20, 10
     RTEXT "text",-1, 155, 55, 20, 10
    
     LTEXT "This is long text example",-1, 65, 70, 55, 15, SS_LEFTNOWORDWRAP|SS_SUNKEN
     LTEXT "This is long text example",-1,125, 70, 50, 15, SS_LEFT|SS_SUNKEN

    Чтобы поменять текст статика ему можно послать сообщение WM_SETTEXT
    (wp=0; lp=(LPARAM)(LPCSTR)lpsz — адрес строки) или
    использовать функции:

    BOOL WINAPI SetWindowText(HWND hw, LPCSTR lpsz)
    BOOL WINAPI SetDlgItemText(HWND hDlg, int idControl, LPCTSTR lpsz)

    Чтобы сменить иконку или картинку нетекстового статика, надо послать
    ему сообщение STM_SETIMAGE (wp=(WPARAM)fImageType
    тип изображения: IMAGE_BITMAP или IMAGE_ICON;
    lp=(LPARAM)(HANDLE)hImage — дескриптор иконки или картинки).

    «BUTTON»
    Кнопка — это небольшое прямоугольное дочернее окно, обычно
    имеющее два состояния: нажато/отпущено или включено/выключено.
    Пользователь меняет состояние этого элемента щелчком мыши.
    К этому классу относятся: кнопки-«давилки» (push buttons),
    кнопки-«галочки» (check boxes), «радио»-кнопки (radio buttons)
    и специальный тип групповых рамочек (group boxes). Примеры:

    Соответствующее описание ресурсов:

     /* DEFPUSHBUTTON - кнопка по умолчанию (нажимается по [Enter]) */
     CONTROL "OK", 2, "BUTTON", BS_DEFPUSHBUTTON, 5, 20, 50, 12
     CONTROL "text", 3, "BUTTON", BS_PUSHBUTTON, 60, 20, 50, 12
    
     CONTROL "GroupBox1", -1, "BUTTON", BS_GROUPBOX, 5, 35, 50, 50
    
     CONTROL "text", 4, "BUTTON", BS_CHECKBOX,     10, 45, 30, 10
     CONTROL "text", 5, "BUTTON", BS_AUTOCHECKBOX, 10, 57, 30, 10
     CONTROL "text", 6, "BUTTON", BS_AUTO3STATE,   10, 69, 30, 10
    
     CONTROL "GroupBox2", -1, "BUTTON", BS_GROUPBOX, 60, 35, 50, 50
    
     CONTROL "text", 7, "BUTTON", BS_AUTORADIOBUTTON|WS_GROUP, 65, 45, 30, 10
     CONTROL "text", 8, "BUTTON", BS_AUTORADIOBUTTON, 65, 57, 30, 10
     CONTROL "text", 9, "BUTTON", BS_AUTORADIOBUTTON, 65, 69, 30, 10

    Для кнопок существуют более простые операторы объявления ресурсов:

     DEFPUSHBUTTON text, id, x, y, width, height [, style]
     PUSHBUTTON text, id, x, y, width, height [, style]
     GROUPBOX text, id, x, y, width, height [, style]
     CHECKBOX text, id, x, y, width, height [, style]
     RADIOBUTTON text, id, x, y, width, height [, style]

    Разница между стилями BS_xxx и BS_AUTOxxx
    заключается в том, что при щелчке по AUTO-кнопкам Windows
    сама автоматически переключает их состояние. Для не AUTO-кнопок
    это надо делать вручную в диалоговой процедуре, послав сообщение
    BM_SETCHECK (wp=(WPARAM)fCheck — флаг:
    BST_UNCHECKED, BST_CHECKED или BST_INDETERMINATE
    (для BS_3STATE-кнопок); lp=0) или при помощи функций:

    BOOL WINAPI CheckDlgButton(HWND hDlg, int idButton, UINT fCheck)
    BOOL WINAPI CheckRadioButton(
        HWND hDlg,         /* дескриптор родительского диалога */
        int idFirstButton, /* id первой радио-кнопки в группе */
        int idLastButton,  /* id последней радио-кнопки в группе */
        int idCheckButton  /* id отмечаемой радио-кнопки */
    )

    Автоматические радио-кнопки должны быть объединены в группу
    при помощи стиля WS_GROUP, чтобы Windows корректно их
    обрабатывала.
    Проверить состояние кнопки можно, послав ей сообщение BM_GETCHECK
    (wp=0; lp=0) или вызовом функции:

    UINT WINAPI IsDlgButtonChecked(HWND hDlg, int idButton)

    При щелчке мыши по кнопке она присылает родительскому диалогу сообщение-оповещение
    WM_COMMAND (HIWORD(wp)=BN_CLICKED; LOWORD(wp)=(int)idButton;
    lp=(HWND)hwndButton
    ).

    «EDIT»
    Поле редактирования предназначено для ввода пользователем текста
    с клавиатуры. Щелчком мыши внутри элемента пользователь передает
    этому элементу фокус ввода (input focus).
    При этом внутри элемента появляется текстовый курсор — мигающая
    каретка. Пользователь может использовать мышь для перемещения
    каретки по полю редактирования и выделению текста в этом поле. Примеры:

    Соответствующее описание ресурсов:

     /* По умолчанию эти элементы создаются вообще без рамки, поэтому добавлено WS_BORDER */
     CONTROL "" 4, "EDIT", ES_MULTILINE|ES_WANTRETURN|WS_BORDER 5, 45, 60, 35
    
     CONTROL "text", 5, "EDIT", ES_LEFT|WS_BORDER, 70, 45, 30, 10
     CONTROL "text", 6, "EDIT", ES_CENTER|ES_PASSWORD|WS_BORDER, 70, 57, 30, 10
     CONTROL "text", 7, "EDIT", ES_RIGHT|ES_READONLY|WS_BORDER, 70, 69, 30, 10

    Стиль ES_WANTRETURN означает, что кнопка [Enter] будет
    обрабатываться самим элементом, а не передаваться диалогу. Благодаря
    этому стилю оказался возможен переход на новую строчку для предложения
    «Она съела кусок…» (на картинке).
    По умолчанию текстовые поля позволяют вводить столько текста,
    сколько может отобразиться в рамках поля. Чтобы предоставить
    пользователю возможность ввести больше текста, надо использовать
    стиль ES_AUTOHSCROLLES_AUTOVSCROLL
    для многострочных полей).
    Для текстовых полей существует более простой оператор объявления ресурсов:

     EDITTEXT id, x, y, width, height [, style]

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

    UINT WINAPI GetDlgItemText(
        HWND hDlg,       /* дескриптор родительского диалога */
        int idControl,   /* идентификатор поля */
        LPSTR lpString,  /* буфер под текст */
        int nMaxCount    /* размер буфера */
    )

    Чтобы узнать размер строки в текстовом поле, надо послать элементу
    сообщение WM_GETTEXTLENGTH (wp=0; lp=0).
    Текстовое поле посылает родительскому диалогу следующие сообщения-оповещения
    WM_COMMAND (LOWORD(wp)=(int)idContol; lp=(HWND)hwndEditCtrl):

    • HIWORD(wp)=EN_KILLFOCUS — текстовое поле потеряло фокус
      (фокус передан другому элементу диалога);
    • HIWORD(wp)=EN_SETFOCUS — текстовое поле получило фокус;
    • HIWORD(wp)=EN_CHANGE — пользователь изменил текст в поле;
    • HIWORD(wp)=EN_ERRSPACE — закончилось место, отведенное под
      текстовый буфер управляющего элемента.
    «LISTBOX»
    Окно-список используется для отображения списка имен
    (например, имен файлов). Пользователь может, просматривая список,
    выделить один или несколько элементов щелчком мыши. При выделении
    того или иного элемента списка, он подсвечивается, а родительскому
    окну посылается сообщение-оповещение. Для очень больших списков
    могут использоваться полосы прокрутки. Примеры:

    Соответствующее описание ресурсов:

     CONTROL "" 3, "LISTBOX", LBS_MULTIPLESEL|WS_BORDER|WS_VSCROLL, 5, 45, 60, 35
     CONTROL "" 4, "LISTBOX", LBS_MULTICOLUMN|WS_BORDER|WS_HSCROLL, 70, 45, 60, 35
     CONTROL "" 5, "LISTBOX", LBS_SORT|LBS_NOSEL|WS_BORDER, 135, 45, 60, 35

    или в короткой форме:

     LISTBOX 3, 5, 45, 60, 35, LBS_MULTIPLESEL|WS_BORDER|WS_VSCROLL
     LISTBOX 4, 70, 45, 60, 35, LBS_MULTICOLUMN|WS_BORDER|WS_HSCROLL
     LISTBOX 5, 135, 45, 60, 35, LBS_SORT|LBS_NOSEL|WS_BORDER

    Для добавления элемента к списку следует послать ему сообщение
    LB_ADDSTRING (wp=0; lp=(LPARAM)(LPCSTR)lpsz
    строка для добавления). Для того, чтобы заполнить один из списков,
    показанных на рисунке, в обработчик сообщения WM_INITDIALOG
    в диалоговую процедуру был вставлен такой фрагмент:

       char *a[12]={
        "jan","feb","mar","apr","may","jun",
        "jul","aug","sep","oct","nov","dec"};
       for (int i=0; i<12; i++) {
        SendDlgItemMessage(hw,3,LB_ADDSTRING,0,(LPARAM)a[i]);
       }

    Кроме этого, список «понимает» следующие сообщения:

    • LB_DELETESTRING (wp=(WPARAM)index; lp=0) —
      удалить элемент с указанным номером;
    • LB_INSERTSTRING (wp=(WPARAM)index; lp=(LPARAM)(LPCSTR)lpsz) —
      вставить указанную строку в список как элемент с индексом index;
    • LB_FINDSTRING (wp=(WPARAM)indexStart; lp=(LPARAM)(LPTSTR)lpszFind) —
      найти элемент, содержащий указанную строку (поиск ведется, начиная
      с элемента indexStart), результат сообщения — номер элемента,
      удовлетворяющего критерию, или LB_ERR;
    • LB_GETCOUNT (wp=0; lp=0) — количество элементов в списке;
    • LB_GETCURSEL (wp=0; lp=0) — выделенный элемент в списке;
    • LB_RESETCONTENT (wp=0; lp=0) — удалить все элементы из списка.

    Окно-список посылает родительскому диалогу следующие сообщения-оповещения
    WM_COMMAND (LOWORD(wp)=(int)idContol; lp=(HWND)hwndListBox):

    • HIWORD(wp)=LBN_DBLCLK — пользователь дважды щелкнул мышью по списку;
    • HIWORD(wp)=LBN_SELCHANGE — пользователь выделил другой элемент
      в списке (или отменил выделение).
    «COMBOBOX»
    Комбобокс — это помесь поля редактирования с окном-списком.
    Этот элемент содержит поле редактирование и список, который может
    отображаться все время либо «выпадать» при нажатии на кнопку рядом
    с полем редактирования. Есть три основных типа комбобоксов:

    • «выпадающий» комбобокс (CBS_DROPDOWN) содержит поле
      редактирования и «выпадающий» список;
    • «выпадающий» список (CBS_DROPDOWNLIST) не содержит
      поля для изменения текста;
    • простой комбобокс (CBS_SIMPLE) содержит поле
      редактирования и обычный список.


    Обратите внимание, что в ресурсах значение высоты элемента определяет
    размер поля редактирования вместе с «выпавшим» списком по вертикали.

     CONTROL "" 3, "COMBOBOX", CBS_DROPDOWN|CBS_AUTOHSCROLL|WS_VSCROLL, 5, 45, 60, 70
     CONTROL "" 4, "COMBOBOX", CBS_DROPDOWNLIST|WS_VSCROLL, 70, 45, 60, 70
     CONTROL "" 5, "COMBOBOX", CBS_SIMPLE|CBS_SORT, 135, 45, 60, 70

    Короткий вариант этого же объявления ресурсов:

     COMBOBOX 3, 5, 45, 60, 70, CBS_DROPDOWN|CBS_AUTOHSCROLL|WS_VSCROLL
     COMBOBOX 4, 70, 45, 60, 70, CBS_DROPDOWNLIST|WS_VSCROLL
     COMBOBOX 5, 135, 45, 60, 70, CBS_SIMPLE|CBS_SORT

    Для работы с комбобоксами существуют сообщения, аналогичные списковым:
    CB_ADDSTRING, CB_DELETESTRING, CB_INSERTSTRING,
    CB_FINDSTRING, CB_GETCOUNT, CB_GETCURSEL,
    CB_RESETCONTENT.
    Комбобокс посылает родительскому диалогу сообщение оповещение WM_COMMAND
    со следующими кодами оповещения:

    • CBN_SELCHANGE, когда пользователь выделяет другую строку
      в комбобоксе (бывает полезным для простых комбобоксов);
    • CBN_SELENDOK, когда пользователь выбрал элемент в выпадающем
      списке и щелкнул мышкой по выделению (подтвердил выделение), для
      простых комбобоксов посылается перед каждым CBN_SELCHANGE;
    • CBN_SELENDCANCEL, когда пользователь закрыл выпадающий список,
      так и не выбрав никакой элемент;
    • CBN_DROPDOWN, когда открывается выпадающий список;
    • CBN_CLOSEUP, когда выпадающий список был закрыт по той или
      иной причине.

    Кроме предопределенных управляющих элементов, Windows предоставляет еще
    набор стандартных управляющих элементов посредством библиотеки
    Common Controls Library (COMCTL32.DLL). Чтобы воспользоваться ей,
    в тест программы надо включить заголовочный файл commctrl.h
    и добавить в блок инициализации программы вызов функции:

    void WINAPI InitCommonControls(void)

    Управляющие элементы из этой библиотеки, как правило, посылают
    сообщения-оповещения родительскому диалогу через сообщение WM_NOTIFY
    (wp=(int)idControl; lp=(LPARAM)(NMHDR*)pmnh — указатель на структуру
    со специльными параметрами сообщения-оповещения).

    Классы управляющих элементов из Common Controls Library:

    List View Controls (WC_LISTVIEW)
    Элемент просмотра списков — это окно отображающее совокупность
    элементов. Каждый элемент может быть представлен текстовой меткой и
    (необязательно) иконкой. Типичный пример использования этого элемента —
    программа «Проводник». Содержимое того или иного каталога представляется
    в виде элемента просмотра списков. Есть четыре основных стиля для этого
    элемента:

    • крупные иконки — стиль LVS_ICON;
    • мелкие иконки — стиль LVS_SMALLICON;
    • список — стиль LVS_LIST;
    • таблица — стиль LVS_REPORT.

     CONTROL "",3,WC_LISTVIEW,LVS_REPORT|WS_BORDER, 5, 45, 60, 70
     CONTROL "",4,WC_LISTVIEW,LVS_LIST|WS_BORDER, 70, 45, 120, 70
     CONTROL "",5,WC_LISTVIEW,LVS_ICON|WS_BORDER, 200, 45, 120, 70

    Приведенные в примере списки заполнялись в диалоговой
    процедуре при инициализации диалога (WM_INITDIALOG):

       /* Создание колонок для 1го списка */
       LV_COLUMN lc; lc.mask=LVCF_FMT|LVCF_TEXT|LVCF_SUBITEM|LVCF_WIDTH;
       lc.fmt=LVCFMT_LEFT;
       lc.pszText="Col1"; lc.iSubItem=0; lc.cx=40;
       SendDlgItemMessage(hw,3,LVM_INSERTCOLUMN,0,(LPARAM)&lc);
       lc.pszText="Col2"; lc.iSubItem=1; lc.cx=40;
       SendDlgItemMessage(hw,3,LVM_INSERTCOLUMN,1,(LPARAM)&lc);
    
       /* Создание списка иконок для 2го и 3го списков */
       HIMAGELIST himl1,himl2;
       himl1=ImageList_Create(16,16,ILC_MASK,1,0);   /* список маленьких иконок */
       ImageList_AddIcon(himl1,LoadIcon(h,"Ex4_Icon"));
       himl2=ImageList_Create(32,32,ILC_MASK,1,0);   /* список больших иконок */
       ImageList_AddIcon(himl2,LoadIcon(h,"Ex4_Icon"));
       SendDlgItemMessage(hw,4,LVM_SETIMAGELIST,LVSIL_SMALL,(LPARAM)himl1);
       SendDlgItemMessage(hw,5,LVM_SETIMAGELIST,LVSIL_NORMAL,(LPARAM)himl2);
    
       /* Заполнение списков */
       LV_ITEM li; li.mask=LVIF_TEXT|LVIF_IMAGE; 
       li.iImage=0; /* номер иконки в списке */
       for (int i=0; i<12; i++) {
        li.iItem=i;
        li.iSubItem=0; li.pszText=a[i];
        SendDlgItemMessage(hw,3,LVM_INSERTITEM,0,(LPARAM)&li);
        SendDlgItemMessage(hw,4,LVM_INSERTITEM,0,(LPARAM)&li);
        SendDlgItemMessage(hw,5,LVM_INSERTITEM,0,(LPARAM)&li);
        wsprintf(str,"%d",i); /* вторая колонка для 1го списка */
        li.iSubItem=1; li.pszText=str;
        SendDlgItemMessage(hw,3,LVM_SETITEM,0,(LPARAM)&li);
       }
    
    Status Windows (STATUSCLASSNAME)
    Поле статуса — это горизонтальное окно в нижней части
    родительского окна, которое программа обычно использует для отображения
    каких-либо характеристик, параметров или небольших текстовых сообщений.

    В примере 4б можно заменить статическое текстовое поле
    на поле статуса:

     Ex4_Dlg DIALOG 50,50,70,40
      STYLE WS_POPUP|WS_CAPTION|WS_BORDER
      CAPTION "MyDlg"
      FONT 10, "Arial"
     {
      CONTROL "OK", 2, "BUTTON", BS_DEFPUSHBUTTON, 5, 10, 60, 12
      CONTROL "Status text",1,STATUSCLASSNAME, 0, 0, 0, 0, 0
     }

    При создании поля статуса не требуется указывать ни размер, ни позицию
    окна, Windows сама выберет эти параметры подходящим образом.
    Для создания поля статуса можно использовать специальную функцию:

      HWND WINAPI CreateStatusWindow(
        LONG style,       /* стиль: обязательно указываем WS_CHILD|WS_VISIBLE */
        LPCTSTR lpszText, /* текст поля статуса */
        HWND hwndParent,  /* родительское окно */
        UINT wID          /* числовой id поля статуса */
      )

    С помощью сообщения SB_SETPARTS поле статуса можно разбить
    на части (wp=(int)nParts — количество частей;
    lp=(LPARAM)(int*)widths — указатель на массив размеров частей).
    В таком случае текст для каждой части поля статуса задается сообщением
    SB_SETTEXT (wp=(int)iPart — номер части;
    lp=(LPARAM)(LPSTR)lpszText — текстовая строка). Пример:

     CreateStatusWindow(WS_CHILD|WS_VISIBLE|SBARS_SIZEGRIP,"text",hw,100);
     int widths[5]={100,150,200,-1};
     SendDlgItemMessage(hw,100,SB_SETPARTS,4,(LPARAM)widths);
     SendDlgItemMessage(hw,100,SB_SETTEXT,2,(LPARAM)"part 2");
     SendDlgItemMessage(hw,100,SB_SETTEXT,3,(LPARAM)"last part");

    Up-Down Controls (UPDOWN_CLASS)
    Управляющий элемент «up-down» представляет собой пару небольших
    кнопок-стрелок, нажимая которые, пользователь увеличивает или уменьшает
    значение. Этот элемент, как правило, связывается с элементом-компаньоном
    (buddy window), обычно реализованным в виде поля редактирования.
    Для пользователя элемент «up-down» и его компаньон представляются единым
    управляющим элементом.

     CONTROL "0", 5, "EDIT", ES_LEFT|WS_BORDER, 5, 30, 30, 10
     CONTROL "", 6, UPDOWN_CLASS, UDS_AUTOBUDDY|UDS_SETBUDDYINT, 35, 30, 10, 10

    Если при создании элемента «up-down» указать стиль UDS_AUTOBUDDY,
    то компаньоном будет назначен предыдущий управляющий элемент диалога.
    Программа может также передать дескриптор окна-компаньона при помощи
    сообщения UDM_SETBUDDY (wp=(WPARAM)(HWND)hwndBuddy
    дескриптор окна-компаньона; lp=0). Если элементу «up-down»
    назначить стиль UDS_SETBUDDYINT, то он будет автоматически
    менять текст окна-компаньона, представляющий числовое значение.
    Другой способ создать элемент «up-down» — использовать функцию

    HWND WINAPI CreateUpDownControl(
      DWORD dwStyle,   /* стиль элемента */
      int x, int y,    /* позиция */	
      int cx, int cy,  /* размеры */
      HWND hParent,    /* дескриптор родительского окна */
      int ID,          /* id элемента */
      HINSTANCE hInst, /* дескриптор экземпляра программы */
      HWND hBuddy,     /* дескриптор окна-компаньона */
      int nUpper,      /* максимальное значение */
      int nLower,      /* минимальное значение */
      int nPos         /* текущее значение */
    )
    Progress Bars (PROGRESS_CLASS)
    Полоса прогресса — это окно, которое программа может использовать
    для индикации состояния выполнения какой-либо длительной операции.
    Окно представляет собой прямоугольник, заполняемый системным цветом
    слева направо.

       CONTROL "",3,PROGRESS_CLASS,WS_BORDER, 5, 45, 100, 10

    Каждый раз, когда приложение посылает этому окну сообщение
    PBM_STEPIT (wp=0; lp=0), заполнение полосы прогресса
    продвигается дальше вправо на некоторое значение.

    Tooltip Controls (TOOLTIPS_CLASS)
    Окно-подсказка — всплывающее окно, содержащее строку описательной
    информации о том или ином элементе интерфейса программы. Таким элементом
    интерфейса может быть конкретное окно (управляющий элемент) или прямоугольный
    участок клиентской области какого-либо окна. Большую часть времени
    подсказка скрыта. Она появляется, когда пользователь задерживат курсор
    мыши над тем или иным элементом интерфейса программы более, чем на полсекунды.
    Подсказка скрывается, когда пользователь кликает мышью или уводит курсор
    с этого элемента. Одно окно-подсказка может обслуживать любое количество
    элементов интерфейса. Чтобы назначить тому или иному элементу интерфейса
    программы подсказку, надо окну-подсказке послать сообщение TTM_ADDTOOL
    (wp=0; lp=(LPARAM)(TOOLINFO*)lpti — указатель на структуру,
    содержащую информацию об элементе). Пример:

      TOOLINFO ti;
      HWND hwTooltip=CreateWindow(TOOLTIPS_CLASS,"",TTS_ALWAYSTIP,
       CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,NULL,NULL,h,NULL);
      ti.cbSize=sizeof(TOOLINFO);
      ti.uFlags=TTF_SUBCLASS|TTF_IDISHWND;
      ti.hwnd=hMainWnd;
      ti.uId=(UINT)GetDlgItem(hMainWnd,ID_MYBUTTON);
      ti.hinst=h;
      ti.lpszText="Tooltip for my button";
      SendMessage(hwTooltip,TTM_ADDTOOL,0,(LPARAM)&ti);

    Property Sheets & Tab Controls
    Элементы вкладки свойств и переключатели вкладок
    обычно используются совместно. Пример использования вкладок:

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

     /* Шаблон вкладки "Общие" */
     ODTab_General DIALOG 0,0,180,150
      CAPTION "Общие"
      STYLE WS_CHILD|WS_VISIBLE
      FONT 8,"Arial"
     {
      GROUPBOX "Интерфейс",-1,5,5,170,40
       PUSHBUTTON "Шрифт температуры",IDC_FONT,20,15,140,12
       AUTOCHECKBOX "На полный экран",IDC_FULLSCR,20,30,140,10,WS_DISABLED
      GROUPBOX "Алгоритм",-1,5,50,170,80
       LTEXT "Приоритет:",-1,15,65,50,10
       CTEXT "priority",IDC_PRIOTEXT,70,56,85,8
       CONTROL "",IDC_PRIOTRACK,TRACKBAR_CLASS,TBS_TOP|TBS_AUTOTICKS,70,64,85,17
       LTEXT "Период опроса датчика:",-1,15,85,110,10
       EDITTEXT IDC_TRATE,125,85,25,10,ES_RIGHT
       LTEXT "мс",-1,152,85,10,10
       LTEXT "Период опроса формирователя:",-1,15,100,110,10
       EDITTEXT IDC_FRATE,125,100,25,10,ES_RIGHT
       LTEXT "мс",-1,152,100,10,10
       LTEXT "Тайм-аут:",-1,15,115,110,10
       EDITTEXT IDC_TIMEOUT,125,115,25,10,ES_RIGHT
       LTEXT "мс",-1,152,115,10,10
     }
     /* Шаблон вкладки "Датчик" */
     ODTab_Sensor DIALOG 0,0,180,150
      CAPTION "Датчик"
      STYLE WS_CHILD|WS_VISIBLE
      FONT 8,"Arial"
     {
      /* ... */
     }
     /* Шаблон вкладки "Порты" */
     ODTab_Ports DIALOG 0,0,200,150
      CAPTION "Порты"
      STYLE WS_CHILD|WS_VISIBLE
      FONT 8,"Arial"
     {
      /* ... */
     }

    Для создания диалога с вкладками используется функция PropertySheet,
    перед вызовом которой надо заполнить соответствующие системные структуры:

     char *TabTemplts[NumTabs]={"ODTab_General","ODTab_Sensor","ODTab_Ports"};
     PROPSHEETPAGE psp[NumTabs]; /* заполняется для каждой вкладки */
     for (i=0; i<NumTabs; i++) {
      psp[i].dwSize=sizeof(PROPSHEETPAGE);
      psp[i].dwFlags=PSP_DEFAULT;
      psp[i].hInstance=hThisInstance;
      psp[i].pszTemplate=TabTemplts[i];
      psp[i].pfnDlgProc=(DLGPROC)GlobDlgProc;
      psp[i].pfnCallback=NULL;
     }
     PROPSHEETHEADER psh;        /* описывает весь диалог */
     psh.dwSize=sizeof(PROPSHEETHEADER);
     psh.dwFlags=PSH_NOAPPLYNOW|PSH_PROPSHEETPAGE;
     psh.hwndParent=hWnd;
     psh.hInstance=hThisInstance;
     psh.pszCaption="Настройки";
     psh.nPages=NumTabs;
     psh.nStartPage=0;
     psh.ppsp=(LPCPROPSHEETPAGE)&psp;
     psh.pfnCallback=NULL;
     PropertySheet(&psh);

    Для каждой вкладки может быть своя диалоговая процедура, а может быть общая
    для всех вкладок (как в этом примере).

    Trackbars (TRACKBAR_CLASS)
    Ползунок (бегунок) используется, если от пользователя
    требуется получить дискретное значение из определенного диапазона.
    Маркер ползунка пермещается на заданное программой значение.
    Пример ползунка показан на первой вкладке предыдущего примера.
    Ползунки бывают горизонтальные (TBS_HORZ) или вертикальные
    (TBS_VERT). Диапазон значений ползунка задается сообщением
    TBM_SETRANGE (wp=(BOOL)fRedraw — перерисовать маркер
    после изменения диапазона; lp=MAKELONG(lMinimum,lMaximum)
    диапазон значений: младшее слово — минимальное значение,
    старшее слово — максимальное значение). Переместить ползунок можно
    при помощи сообщения TBM_SETPOS (wp=TRUE;
    lp=(LONG)position — новая позиция ползунка). Чтобы получить
    текущее значение ползунка, следует послать ему сообщение TBM_GETPOS
    (wp=0; lp=0). Ползунок оповещает родительское окно о событиях
    через сообщение WM_HSCROLL (LOWORD(wp)=ScrollCode
    код события; HIWORD(wp)=posistion — позиция маркера ползунка;
    lp=(HWND)hwndTrackBar — дескриптор элемента).

    Toolbars (TOOLBARCLASSNAME)
    Панель инструментов — это окно, содержащее набор кнопок,
    посылающих командное сообщение родительскому окну, когда пользователь
    щелкает по ним. Как правило, кнопки на панели инструментов соответствуют
    часто используемым командам меню приложения. Панель инструментов
    располагается ниже строки меню.

    Информация о кнопках передается панели инструментов через структуру
    TBBUTTON, а для создания окна панели инструментов удобно
    использовать функцию CreateToolbarEx.

       TBBUTTON tbb[nButtons];
       /* 1я кнопка */
       tbb[0].iBitmap=STD_PROPERTIES;   /*id иконки*/
       tbb[0].idCommand=ID_OPTION;      /*id команды*/
       tbb[0].fsState=TBSTATE_ENABLED;  /*состояние*/
       tbb[0].fsStyle=TBSTYLE_BUTTON;   /*стиль*/
       tbb[0].iString=0;                /*подпись под кнопкой*/
       /* 2я кнопка */
       tbb[1].iBitmap=STD_REDOW;
       tbb[1].idCommand=ID_CALCOUT;
       tbb[1].fsState=TBSTATE_ENABLED;
       tbb[1].fsStyle=TBSTYLE_BUTTON; 
       tbb[1].iString=0;
       /* 3я кнопка */
       tbb[2].iBitmap=STD_UNDO;
       tbb[2].idCommand=ID_CALCIN;
       tbb[2].fsState=TBSTATE_ENABLED;
       tbb[2].fsStyle=TBSTYLE_BUTTON;
       tbb[2].iString=0;
       /* разделитель */
       tbb[3].fsStyle=TBSTYLE_SEP;
       /* и т.д. */
       HWND htb=CreateToolbarEx(
        (HWND) hWndMain,                /*дескриптор родительского окна*/
        (DWORD) WS_CHILD|WS_VISIBLE|TBSTYLE_TOOLTIPS, /*стиль*/
        (UINT) ID_TOOLBAR,              /*id окна*/
        (int) 15,                       /*количество иконок в указанном ресурсе*/
        (HINSTANCE) HINST_COMMCTRL,     /*дескриптор модуля, из которого берется ресурс с иконками*/
        (UINT) IDB_STD_SMALL_COLOR,     /*id ресурса с иконками*/
        (TBBUTTON*) tbb,                /*указатель на массив с информацией о кнопках*/
        (int) nButtons,                 /*количество кнопок на панели*/
        16,16,                          /*размеры кнопок*/
        15,15,                          /*размеры иконок для кнопок*/
        sizeof(TBBUTTON)                /*размер структуры TBBUTTON*/
       );
    Rich Edit Controls (RICHEDIT_CLASS)
    Продвинутое поле редактирования является развитием
    класса «EDIT» стандартных управляющих элементов. Элементы управления
    этого класса поддерживают форматирование текста (по отдельным символам и
    по отдельным абзацам) и позволяют внедрять OLE-объекты.

    Tree View Controls (WC_TREEVIEW)
    Элемент просмотра дерева позволяет представлять информацию
    об иерархии некоторых объектов (содержание документа, дерево каталогов
    файловой системы и т.п.) Каждый объект может быть представлен текстовой
    меткой и иконкой. Объект может иметь иерархию дочерних объектов,
    которая раскрывается по щелчку на этом элементе.

  • Стандартные диалоги
  • Windows предоставляет набор готовых стандартных диалогов посредством
    библиотеки Common Dialog Boxes Library (COMDLG32.DLL): диалог открытия
    и сохранения файла, диалог печати документа, диалог выбора цвета, шрифта и т.п.
    Чтобы создать один из перечисленных диалогов, надо заполнить определенную
    структуру и вызвать соответствующую функцию из этой библиотеки:

    • BOOL WINAPI ChooseColor(CHOOSECOLOR* lpcc)
      — создает диалог, отображающий палитру цветов и позволяющий
      пользователю выбрать тот или иной цвет или создать свой.
    • BOOL WINAPI ChooseFont(CHOOSEFONT* lpcf)
      — создает диалог, отображающий имена установленных в системе шрифтов,
      их кегль, стиль начертания и т.п.
    • BOOL WINAPI GetOpenFileName(OPENFILENAME* lpofn)
      и BOOL WINAPI GetSaveFileNAme(OPENFILENAME* lpofn)
      — создают диалог, отображающий содержимое того или иного каталога,
      и позвояющий пользователю выбрать уникальное имя файла для открытия или
      сохранения.
    • BOOL WINAPI PrintDlg(PRINTDLG* lppd)
      — создает диалог, позволяющий пользователю установить различные
      опции печати, например, диапазон страниц, количество копий и др.
    • BOOL WINAPI PageStupDlg(PAGESETUPDLG* lppsd)
      — создает диалог, позволяющий пользователю выбрать различные
      параметры страницы: ориентацию, поля, размер бумаги и т.п.
    • HWND WINAPI FindText(FINDREPLACE* lpfr)
      — создает диалог, позволяющий пользователю ввести строку для поиска
      и такие опции, как направление поиска.
    • HWND WINAPI ReplaceText(FINDREPLACE* lpfr)
      — создает диалог, позволяющий пользователю ввести строку для поиска,
      строку для замены и опции замены (направление поиска, область поиска).

    Все перечисленные диалоги, кроме последних двух, — модальные,
    т.е. указанная функция не вернет значение, пока пользователь тем или
    иным способом не закроет диалог. Значение TRUE означает,
    что пользователь закрыл диалог, нажав на «ОК», а соответствующая структура
    заполнена новыми значениями. Значение FALSE означает,
    что пользователь нажал на [Esc], выбрал команду системного меню «Закрыть»
    или нажал кнопку «Отмена», а соответствующая структура осталась неизменной.
    Пример использования диалога открытия файла:

      char filename[MAX_PATH]="";             /*буфер под имя файла*/
      OPENFILENAME of;
      of.lStructSize=OPENFILENAME_SIZE_VERSION_400A; /*размер структуры OPENFILENAME*/
      of.hwndOwner=hw;                        /*дескриптор родительского окна*/
      of.hInstance=h;                         /*дескриптор экземпляра программы*/
      of.lpstrFilter="All files (*.*)*.*";/*фильтр файлов (тип)*/
      of.lpstrCustomFilter=NULL;              /*еще один фильтр: нам не надо*/
      of.nMaxCustFilter=0;                    /*нам не надо*/
      of.nFilterIndex=1;                      /*количество заданных нами фильтров*/
      of.lpstrFile=filename;                  /*адрес буфера под имя файла*/
      of.nMaxFile=MAX_PATH;                   /*размер буфера под имя файла*/
      of.lpstrFileTitle=NULL;                 /*буфер под рекомендуемый заголовок: нам не надо*/
      of.nMaxFileTitle=0;                     /*нам не надо*/
      of.lpstrInitialDir=NULL;                /*стартовый каталог: текущий*/
      of.Flags=OFN_PATHMUSTEXIST|OFN_FILEMUSTEXIST|OFN_HIDEREADONLY; /*разные флаги*/
      if (GetOpenFileName(&of)) {
       /* действия в случае успешного выбора файла */
      }
    

  • Немодальный диалог в качестве главного окна
  • Ресурсы значительно облегчают работу программиста по созданию интерфейса
    программы, поэтому весьма привлекательно использовать ресурсы для описания
    главного окна. Это возможно в случае, если главное окно реализовано в виде
    немодального диалога.

    Пример 5 демонстрирует использование немодального диалога
    в приложении типа «блокнот».

    Файл example5.h содержит константы-идентификаторы команд меню
    и элементов диалога.

    #define IDC_OPEN	10
    #define IDC_SAVE	11
    #define IDC_SAVEAS	12
    #define IDC_EXIT	13
    #define IDC_ABOUT	14
    
    #define ID_EDIT		20
    #define ID_STATUS	21

    Файл example5.rc описывает ресурсы программы: иконку, меню и шаблон диалога.

    #include "example5.h"
    
    Ex5_Icon ICON "myicon.ico"
    
    Ex5_Menu MENU
    {
     POPUP "&File"
     {
      MENUITEM "&Open...", IDC_OPEN
      MENUITEM "&Save", IDC_SAVE
      MENUITEM "Save &As...", IDC_SAVEAS
      MENUITEM SEPARATOR
      MENUITEM "&ExittAlt-F4", IDC_EXIT
     }
     POPUP "&Help"
     {
      MENUITEM "&About...", IDC_ABOUT
     }
    }
    
    Ex5_Dlg DIALOG 50,50,300,200
     STYLE WS_OVERLAPPED|WS_CAPTION|WS_BORDER|WS_SYSMENU|WS_VISIBLE
     MENU "Ex5_Menu"
     CAPTION "Example 5"
     FONT 10, "Arial"
    {
     EDITTEXT ID_EDIT, 5, 5, 290, 180, ES_MULTILINE|ES_WANTRETURN|ES_AUTOHSCROLL|ES_AUTOVSCROLL
     CONTROL "", ID_STATUS, STATUSCLASSNAME, 0, 0, 0, 0, 0
    }

    Файл example5.cpp — текст программы.

    #include <windows.h>
    #include <commctrl.h>
    #include <stdlib.h>
    #include "example5.h"
    
    BOOL CALLBACK MainProc(HWND,UINT,WPARAM,LPARAM);
    
    HINSTANCE hThisInstance;
    char filename[MAX_PATH]=""; /*буфер имени файла*/ 
    
    int WINAPI WinMain(HINSTANCE hInst,HINSTANCE,LPSTR,int) {
     hThisInstance=hInst;
     InitCommonControls();
     HWND hMainWnd=CreateDialog(hInst,"Ex5_Dlg",NULL,(DLGPROC)MainProc);
     if (!hMainWnd) return FALSE;
     MSG msg;                   /*цикл обработки событий*/
     while (GetMessage(&msg,NULL,0,0)) {
      if (!IsDialogMessage(hMainWnd,&msg)) {
       TranslateMessage(&msg); 
       DispatchMessage(&msg); 
      }
     }
     return msg.wParam; 
    }
    
    /* диалоговая процедура */
    BOOL CALLBACK MainProc(HWND hw,UINT msg,WPARAM wp,LPARAM lp) {
     static DWORD OldIcon=0;    /* id старой иконки диалога */
     static OPENFILENAME of;
     char* buf;
     HANDLE hf; 
     DWORD len,len1;
     switch (msg) {
      case WM_INITDIALOG:       /* меняем иконку диалога */
       OldIcon=SetClassLong(hw,GCL_HICON,(long)LoadIcon(hThisInstance,"Ex5_Icon"));
       return TRUE;
      case WM_COMMAND:
       switch (LOWORD(wp)) {
        case IDCANCEL:          /* посылается при закрытии диалога по [Esc]*/
        case IDC_EXIT:          /* команда меню "Exit" */
         DestroyWindow(hw);
         break;
        case IDC_OPEN:          /* команда меню "Open" */
         of.lStructSize=OPENFILENAME_SIZE_VERSION_400A;
         of.hwndOwner=hw;
         of.lpstrFilter="All files (*.*)*.*";
         of.lpstrCustomFilter=NULL; of.nMaxCustFilter=0;
         of.nFilterIndex=1;
         of.lpstrFile=filename; of.nMaxFile=MAX_PATH;
         of.lpstrFileTitle=NULL; of.nMaxFileTitle=0;
         of.lpstrInitialDir=NULL;
         of.Flags=OFN_PATHMUSTEXIST|OFN_FILEMUSTEXIST|OFN_HIDEREADONLY;
         if (!GetOpenFileName(&of)) break;
         SetDlgItemText(hw,ID_STATUS,filename);
         /* открываем файл */
         hf=CreateFile(filename,GENERIC_READ,0,NULL,OPEN_EXISTING,0,NULL);
         if (hf==INVALID_HANDLE_VALUE) {
          MessageBox(hw,"Open failed","Error",MB_ICONHAND|MB_OK);
          break;
         }
         len=GetFileSize(hf,NULL);
         buf=(char*)malloc(len+1); /* доп. байт под символ-терминатор (0) */
         if (!buf) {
          MessageBox(hw,"Mem alloc failed","Error",MB_ICONHAND|MB_OK);
          break;
         }
         ReadFile(hf,buf,len,&len1,NULL);
         buf[len1]=0;
         CloseHandle(hf);
         SetDlgItemText(hw,ID_EDIT,buf);
         free(buf);
         break;
        case IDC_SAVEAS:        /* команда меню "Save As" */
         of.lStructSize=OPENFILENAME_SIZE_VERSION_400A;
         of.hwndOwner=hw;
         of.lpstrFilter="All files (*.*)*.*";
         of.lpstrCustomFilter=NULL; of.nMaxCustFilter=0;
         of.nFilterIndex=1;
         of.lpstrFile=filename; of.nMaxFile=MAX_PATH;
         of.lpstrFileTitle=NULL; of.nMaxFileTitle=0;
         of.lpstrInitialDir=NULL;
         of.Flags=OFN_PATHMUSTEXIST|OFN_OVERWRITEPROMPT|OFN_HIDEREADONLY;
         if (!GetSaveFileName(&of)) break;
        case IDC_SAVE:          /* команда меню "Save" */
         if (lstrlen(filename)==0) {
          /* для нового файла - вызываем диалог "Save As" */
          PostMessage(hw,WM_COMMAND,IDC_SAVEAS,lp);
          break;
         }
         SetDlgItemText(hw,ID_STATUS,filename);
         /* сохраняем файл */
         hf=CreateFile(filename,GENERIC_WRITE,0,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
         if (hf==INVALID_HANDLE_VALUE) {
          MessageBox(hw,"Save failed","Error",MB_ICONHAND|MB_OK);
          break;
         }
         len=SendDlgItemMessage(hw,ID_EDIT,WM_GETTEXTLENGTH,0,0);
         buf=(char*)malloc(len+1); /* доп. байт под символ-терминатор (0) */
         GetDlgItemText(hw,ID_EDIT,buf,len+1);
         if (!buf) {
          MessageBox(hw,"Mem alloc failed","Error",MB_ICONHAND|MB_OK);
          break;
         }
         WriteFile(hf,buf,len,&len1,NULL);
         CloseHandle(hf);
         free(buf);
         break;
        case IDC_ABOUT:         /* команда меню "About" */
         MessageBox(hw,"Example N5 from http://dims.karelia.ru/win32","About",MB_OK|MB_ICONINFORMATION);
         break;
       }
       return TRUE;
      case WM_DESTROY:          /* при закрытии окна восстанавливаем старую иконку */
       SetClassLong(hw,GCL_HICON,(long)OldIcon);
       PostQuitMessage(0);
       return TRUE;
     }
     return FALSE;
    }

    Понравилась статья? Поделить с друзьями:
  • Основы организации операционных систем microsoft windows ответы интуит
  • Основы диагностики сети консольным средствами ос windows
  • Основы windows server 2008 r2 видео
  • Основы windows server 2008 active directory
  • Основы windows presentation foundation крис андерсон pdf