Концепции программирования для Windows

Рубрика: Visual Studio, Дата: 9 August, 2014, Автор:

windowsЗдорова господа!

Решил написать статью в которой пойдет речь о том как создаются программы для windows в vusual C++ 2010.

При работе в среде разработки Visual C++ 2010 доступны три основных способа создания интерактивных приложений Windows.

  • Использование интерфейса API Windows. Это базовый интерфейс, предоставляемый операционной системой для взаимодействия с приложением, выполняющимся под ее управлением.
  • Использование классов Microsoft Foundation Classes, известных также как библиотека MFC, Это набор классов на языке С++, инкапсулирующих интерфейс API Windows.
  • Использование Windows Forms. Это основанный на формах механизм разработки для создания приложений, выполняющихся под управлением среды CLR

Эти три подхода перечислены в порядке от требующего наибольших усилий программирования до наименьших. С помощью интерфейса API Windows вы пишете абсолютно весь код, – все элементы, составляющие графический пользовательский интерфейс (GUI)
Содержание:

1. Основы программирования для Windows
2. Элементы окна Windows
3. Программы Windows и операционная система
4. Программирование, управляемое событиями
5. Сообщения Windows
6. Интерфейс API Windows
7. Типы данных Windows
8. Нотация программ Windows
9. Структура программы Windows
10. Функция WinMain().
11. Указание вида окна программы.
12. Создание окна программы.
13. Инициализация окна программы.
14. Очередизованные и неочередизованные сообщения.
15. Цикл сообщений.
16. Многозадачность.
17. Полная функция WinMain().
18. Функции обработки сообщений.
19. Функция WindowProc().
20. Декодирование сообщения Windows.
21. Рисование клиентской области окна.
22. Завершение программы.
23. Простая программа Windows.
24. Огранизация программ Windows.
25. Библиотека Microsoft Foundation Classes.
26. Нотация библиотеки MFC.
27. Структуризация программы MFC.
28. Минимальное приложение MFC.
29. Использование Windows Forms.
30. Подвод итогов.


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

вашего приложения, должны быть созданы программно. При использовании  библиотеки MFC вы получаете некоторую помощь в создании GUI, а именно: можете собирать элементы управления в диалоговом окне графически и программировать взаимодействие с пользователем; однако вам все еще придется писать много кода. Примененяя Windows Forms, вы сможете создать полный интерфейс GUI, в том числе главное окно приложения, графически собирая элементы управления, с которыми взаимодействует пользователь. Вы просто помещаете их в требуемые места окна формы, а их код создается автоматически. Использование Windows Forms – самый быстрый и легкий способ создания приложения, поскольку объем кода, который потребуется написать, значительно меньше по сравнению с двумя другими подходами. Код приложения Windows Forms также пользуется всеми преимуществами выполнения под управлением среды CLR.

Использование библиотеки MFC предполагает большие усилия по программированию, чем Windows Forms, но предлагает при этом и большие возможности по контролю процесса создания интерфейса GUI программ, выполняемых на ПК непосредственно. Поскольку применение интрефейса API Windows – наиболее трудоемкий способ разработки приложений, мы не станем вдаваться в его подробности. Однако мы получим достаточные представления об интерфейсе API Windows, чтобы понять принципы механизма, который позволяет всем приложениям Windows, скрытым образом взаимодействовать с операционной системой.

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

Элементы окна Windows

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

Программы Windows и операционная система

Когда вы пишете код для Windows, ваша программа подчиняется операционной системе, и Windows управляет ею. Ваша программа не должна на прямую взаимодействовать с оборудованием, и все коммуникации с внешним миром должны проходить через операционную систему Windows. Когда вы используете программу Windows, то первоначально взаимодействуете с операционной системой Windows, а она уже взаимодействует с прикладной программой. Ваша программа Windows – это “хвост”, а сама операционная система Window – “собака”, и “виляние хвостом” происходит только тогда, когда операционная система Windows приказывает делать это.

Программирование, управляемое событиями

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

Сообщения Windows

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

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

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

Интерфейс API Windows

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

Интерфейс API Windows покрывает все аспекты взаимодействия операционной системы Windows и приложения. Количество функции в API Windows и использование их достаточно сложно – иногда не просто даже понять что делает та или иная функция. Visual C++ 2010 упаковывает интерфейс API Windows в MFC (Microsoft Foundation Classes), что облегчает его использование.

Типы данных Windows

наиболее распространенные типы данных Windows

type windows

Нотация программ Windows

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

Примеры префиксов указывающих вид значений в переменных

pref_vengrAPI Windows все еще использует венгерскую нотацию.

Структура программы Windows

Для минимальной программы Windows, использующей интерфейв API Windows, следует написать две функции. Это функция WinMain(), с которой начинается выполнение программы и происходит ее основная инициализация, и функция WindowProc(), вызываемая самой операционной системой Windows для обработки сообщений приложения. Часть WindowProc() программы Windows – обычно большая ее часть, поскольку это место, в котором находится специфичный для приложения код, отвечающий на сообщения, инициированные вводом пользователя того или иного рода. Эти две функции образуют полную программу, но они не связаны между собой непосредственно. Функция WinMain() не вызывает функцию WindowProc(), это делает сама операционная система Windows. Фактически Windows вызывает также и функцию WinMain(). Это проиллюстрировано на рисунке нижеQIP Shot - Screen 159Функция WinMain() взаимодействует с Windows, вызывая некоторые из функций интерфейса API Windows. То же самое относиться к функции WindowProc(). Интегрирующим фактором вашей программы Windows выступает сама операционная система Windows, которая связана и с функцией WinMain(), и с функцией WindowProc().

Функция WinMain()

Функция WinMain() – это эквивалент функции main() консольной программы. Именно здесь начинается выполнение и происходит базовая инициализация остальной части программы. Чтобы позволить операционной системе Windows передать ей данные, функция WinMain() получает четыре параметра и возвращает значение типа int. ЕЕ прототип выглядит следующим образом.

int WINAPI WinMain(HINSTANCE hInstance,
                              HINSTANCE hPrevInstance,
                              LPSTR lpCmdLine,
                              int nCmdShow
                             );

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

Если вас действительно интересует соглашения о вызовах, то они описаны в документации, поставляемой со средой разработки Visual C++ 2010, Макросс WINAPI определен как __stdcall, а этот модификатор перед именем функции означает, что долно использоваться стандартное соглашение о вызовах Windows.

Четыре аргумента, передаваемых Windows вашей функции WinMain(), содержат важные данные. Первый аргумент, hInstance, имеет тип HINSTANCE, представляющий дескриптор экземпляра – выполняющей программы.

Следующий аргумент hPrevInstance, унаследован от 16-разрядных версий операционной системы Windows. В среде Windows 3.x этот параметр передавал дескриптор предыдущего экземпляра программы, если таковой имелся. Если значение аргумента hPrevInstance равно NULL, вы знали что данная программа запущена впервые и в конкретный момент в системе имеется только один ее экземпляр.

Следующий аргумент, lpCmdLine, – это указатель на строку, содержащую командрую строку, запустившую программу. Например, если вы запускаете ее, используя пункт Run(Выполнить) меню Start(Пуск) в Windows, эта строка содержит все, что было указано в поле Open(Открыть). Наличие этого указателя позволяет получить любые значения параметров, которые могут появиться в командной строке.

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

Функция WinMain() вашей программы Windows должна выполнять четыре действия, которые перечислены ниже.

  • Сообщать операционной системе Windows, какого вида окно требуется вашей программе.
  • Создавать окно программы.
  • Инициализировать окно программы.
  • Извлекать сообщения Windows, предназначенные программе.

Рассмотрим все эти действия по очереди, а затем создадим полную функцию WinMain().

Указание вида окна программы

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

Определение структуры WNDCLASSEX

struct WNDCLASSEX
{
  UINT cbSize;    //Размер этого объекта в байтах
  UINT style;       //Стиль окна
  WNDPROC lpfnWndProc;  //Указатель на функцию обработки сообщений
  int cbClsExtra; //Дополнительный байт после класса окна
  int cbWndExtra; //Дополнительные байты после экземпляра окна
  HINSTANCE bInstance; //Дескриптор экземпляра приложения
  HICON hIcon; //Пиктограмма приложения
  HCURSOR hCursor;  //Курсор приложения
  HBRUSH hbrBackground; //Кисть, определяющая цвет фона
  LPCTSTR lpszMenuName; //Указатель на имя ресурса меню
  LPCTSTR lpszClassName; //Указатель на имя класса
  HICON hIconSm;  //Малая пиктограмма, связанная с окном
};

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

WNDCLASSEX WindowClass;//Создать объект класса окна

Все, что необходимо сделать – это заполнить значения членов структуры WindowClass. Установка члена структуры cbSize упрощается за счет применения оператора sizeof.

WindowClass.cbSize=sizeof(WNDCLASSEX);

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

Все возможные значения константы стиля можно найти по слову WNDCLASSEX в библиотеке MSDN, которая доступна по адрессу http://msdn2.microsoft.com/en-us/library.

Когда требуется две или несколько констант, их можно объединить с использованием оператора ИЛИ (|) и получить составное значение.

WindowClass.style=CS_HREDRAW|CS_VREDRAW;

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

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

WindowClass.lpfnWndProc=WindowProc;

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

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

Члены hIcon, hCursor и hbrBackground – это дескрипторы, которые соответственно определяют пиктограмму, представляющую приложение в свернутом виде, курсор мыши, используемый окном, и цвет фона клиентской области окна. Они устанавливаются с использованием функции API Windows. Соответствующий пример приведен ниже.

WindowClass.hIcon=LoadIcon(0,IDI_APPLICATION);
WindowClass.hCursor=LoadCursor(0,IDC_ARROW);
WindowClass.hbrBackground=static_cast<HBRUSH>(GetStockObject(GRAY_BRUSH));

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

Член lpszMenuName задает имя ресурса, определяющего меню окна, а значение 0 означает отсутствие меню у окна.

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

static LPCTSTR szAppName=L"OFWin";//Определить имя класса окна
WindowClass.lpszClassName=szAppName;//Установить имя класса

Здесь переменная szAppName определена как строка Unicode. Фактичести тип LPCSTR определен как const wchar_t*, если для приложения определено UNICODE, и как const char* – в противном случае. Таким образом, определение переменной szAppName предполагает приложение, использующее символы Unicode.

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

Создание окна программы

После присвоения нужных значений всем членам структуры WNDCLASSEX об этом нужно сообщить операционной системе Windows. Для этого используется функция API Windows RegisterClassEx(). Если структура называется WindowClass, то опреатор, с помощью которого это делается, должен быть таким.

RegisterClassEx(&WindowClass);

Адрес структуры передается в функцию, а операционная система Windows извлекает и сохраняет все значения, установленные в членах структуры. Этот процесс называется регистрацией класса окна.

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

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

HWND hWnd;//Дескриптор окна
...
hWnd=CreateWindow(
                                 szAppName, //Имя класса окна
                                 "A Basic Window the Hard Way", //Заголовок окна
                                 WS_OVERLAPPEDWINDOW, //Стиль окна - перекрываемое
                                  CW_USEDEFAULT, //Позиция на экране по умолчанию
                                  CW_USEDEFAULT, //левого верхнего угла как x,y...
                                  CW_USEDEFAULT, //Стандартный размер окна - ширина...
                                  CW_USEDEFAULT, //... и высота
                                  0, //Нет родительского окна
                                  0, //Нет меню
                                  hInstance, //Дескриптор экземпляра программы
                                  0 //Никаких данных для создания окна
                                 );

hWnd типа HWND – это 32-разрядный целочисленный дескриптор окна. Мы используем эту переменную для записи значения, возвращенного функцией CreateWindow(), которое идентифицирует окно. Первый аргумент который передается в функцию, – это имя класса. Оно применяется операционной системой Windows для идентификации структуры WNDCLASSEX, которую мы передали для этого при вызове функции RegisterClassEx(), так что информация из этой структуры может быть использована в процессе создания окна.

Второй аргумент функции CreateWindow() определяет текст, который должен появится в панели заголовка. Третий аргумент задает стиль, который имеет окно после создания. Указанное здесь значение, WS_OVERLAPPEDWINDOW, в действительности комбинирует несколько флагов. Оно определяет окно как обладающее стилями WS_OVERLAPPED, WS_CAPTION, WS_SYSMENU, WS_THICKEFRAME, WS_MINIMIZEBOX и WS_MAXIMIZEBOX. Это дает в результате перекрываемое окно, предназначенное служить главным окном приложения, с панелью заголовка на толстой рамке, имеющей пиктограмму, системное меню, а также кнопки разворачивания и сворачивания. Окно имеет толстую линию рамки с помощью котором можно изменять его размеры.

Следующие четыре аргумента определяют положение и размеры окна на экране. Первые два – это экранные координаты левого оверхнего угла окна, а вторые два – ширина и высота окна. Значение CW_USEDEFAULT указывает операционной системе Windows применить стандартные значения окна. Значение CW_USEDEFAULT применяется только к окнам, заданным как WS_OVERLAPPED.

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

Следующий аргумент также установлен в нуль, и это говорит о том, что никакого меню не требуется.

Затем необходимо указать дескриптор текущего экземпляра программы, который был передан данной программе от операционной системы Windows (hInstance)

Последний аргумент при создании окна равен нулю, поскольку в данном примере нужно простое окно. Если захотите создать клиентское окно многодокументного интерфейса (MDI – multiple-document interface), то последний аргумент должен указывать на структуру связанную с этим.

Интерфейс API Windows включает также функцию CreateWindowEx(), используемую для создания окон с расширенной информацией о стиле.

После вызова функции CreateWindow() окно существует, но пока не отображается на экране. Чтобы отобразить его, необходимо вызвать другую фукнцию API Windows.

ShowWindow(hWnd,nCmdShow);//Отобразить окно

Первый аргумент идентифицирует окно и равен дескриптору, возвращенному функцией CreateWindow(). Второй имеет значение nCmdShow, переданное функцией WinMain(), и идентифицирует способ отображения окна на экране.

Инициализация окна программы

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

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

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

UpdateWindow(hWnd);//Вызвать перерисовку клиентской области окна

Эта функция принимает один аргумент – дескриптор окна hWnd, который идентифицирует конкретное окно программы.

Очередизованные и неочередизованные сообщения

Существует два вида сообщений Windows: очередизованные и неочередизованные.

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

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

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

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

MSG msg; //Структура сообщения Windows
while(GetMessage(&msg,0,0,0)==TRUE)//Получить сообщение
{
  TranslateMessage(&msg);// Преобразование сообщения
  DispatchMessage(&msg);//Диспетчеризация сообщения
}

Обработка сообщения включает три этапа.

  • Функция GetMessage(). Получает сообщение из очереди.
  • Функция TranslageMessage(). Выполняет все необходимые преобразования извлеченного сообщения.
  • Функция DispatchMessage(). Заставляет операционную систему Windows вызвать функцию WindowProc() вашего приложения для обработки сообщения.

Функция GetMessage() извлекает сообщение, помещенное в очередь окна приложения, и сохраняет информацию о сообщении в переменной msg, на которую указывает первый аргумент. Переменная msg, представляет собой структуру типа MSG, содержит множество членов, которые здесь не используются. Рассмотрим определение этой структуры.

struct MSG
{
  HWND hwnd;//Дескриптор окна
  UINT message; //Идентификатор сообщения
  WPARAM wParam; //Параметр сообщения (32-битовый)
  LPARAM lParam; //Параметр сообщения (32-битовый)
  DWORD time; //Время, когда сообщение было помещено в очередь
  POINT pt;  //Положение курсора мыши
};

Точное значение членов wParam и lParam зависит от типа сообщения. Идентификатор (ID) сообщения в члене message – это целочисленное значение, которое может быть одним из набора значений, предопределенных в файле заголовка windows.h в виде символических констант. Идентификаторы сообщений для основных окон начинаются с префикса WM_, а типичные примеры приведены в таблице ниже.QIP Shot - Screen 160Функция GetMessage() всегда возвращает значение TRUE, если только не получено сообщение WM_QUIT для завершения программы, тогда она возвращает значение FALSE, при отсутствии ошибок возвращается значение -1. Таким образом, цикл while продолжается до тех пор, пока не будет создано сообщение о выходе для закрытия приложения либо не возникнет ошибка. В обоих случаях необходимо завершить программу передав значение wParam обратно в операционную систему Windows с помощью оператора return.

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

Второй аргумент функции GetMessage() – это дескриптор окна, для которого вы хотите получить сообщение. Этот параметр может быть использован для извлечения сообщений одного окна отдельно от другого. Если аргумент равен 0, как здесь, то функция GetMessage() извлекает все сообщения для приложения. Это простой способ извлечения всех сообщений для приложения, независимо от того, сколько окон оно имеет.

Последние два аргумента функции GetMessage() – это целые числа, содержащие минимальное и максимальное значения идентификаторов сообщений, которые вы хотите извлечь из очереди.

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

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

В современных версиях Windows операционная система может прервать приложение после истечения периона времени и передать управление другому приложению. Этот механизм называется вытесняющей многозадачностью, поскольку приложение может быть прервано в любом событии. При вытесняющей многозадачности вы все равно должны программировать цикл сообщений в функции WinMain(), используя функцию GetMessage(), как и ранее, и оставляя возможность время от времени передавать управление процессором системе Windows при долго выполняющихся вычислениях (это обычно делается с помощью функции PeekMessage() интерфейса API). Если вы не сделаете этого, ваше приложение может оказаться не в состоянии реагировать на сообщения перерисовки окна, когда оно возникнет. Это может быть связано с причинами, мало зависящими от вашего приложения, например, когда закрывается перекрывающееся окно другого приложения.

Концептуальная структура функции GetMessage() показана ниже.QIP Shot - Screen 161В цикле while первый вызов функции TranslageMessage() запрашивает у операционной системы Windows выполнение некоторой работы по преобразованию сообщений, имеющих отношение к клавиатуре. Затем вызов функции DispatchMessage() заставляет операционную систему Windows осуществить диспетчеризацию сообщения, или другими словами, вызывать функцию WindowProc() в вашей программе для обработки сообщения. Сообщения WM_QUIT говорит о том, что программа должна завершиться, потому в приложение возвращается значение FALSE, что останавливает цикл сообщений.

Полная функция WinMain()

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

Показать »

//Листинг OFWIN 1
#include <Windows.h>

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
				   LPSTR lpCmdLine, int nCmdShow)
{
	WNDCLASSEX WindowClass; //Структура для хранения артибутов окна

	static LPCTSTR szAppName=L"OFWin";//Опеределить класс окна
	HWND hWnd;//Дескриптор окна
	MSG msg;//Структура сообщения окна

	//Установить размер структуры
	WindowClass.cbSize=sizeof(WNDCLASSEX);

	//Перерисовать окно при изменении размера
	WindowClass.style=CS_HREDRAW|CS_VREDRAW;

	//Определить функцию - обработчик сообщений
	WindowClass.lpfnWndProc=WindowProc;

	WindowClass.cbClsExtra=0;//Никаких дополнительных байтов после
	WindowClass.cbWndExtra=0;//структуры класса окна в экземпляре
							 //окна

	WindowClass.hInstance=hInstance;//Дескриптор экземпляра
									//приложения

	//Установить пиктограмму приложения по умолчанию
	WindowClass.hIcon=LoadIcon(0,IDI_APPLICATION);

	//Установить стандартный курсор мыши в виде стрелочки
	WindowClass.hCursor=LoadCursor(0,IDC_ARROW);

	//Установить серую кисть для риосвания фона
	WindowClass.hbrBackground=static_cast<HBRUSH>(GetStockObject(GRAY_BRUSH));

	WindowClass.lpszMenuName=0;//Нет меню
	WindowClass.lpszClassName=szAppName;//Установить имя класса
	WindowClass.hIconSm=0;//маленькая пиктограмма по умолчанию

	//Теперь зарегистрировать класс окна
	RegisterClassEx(&WindowClass);

	//Теперь можно создать окно
	hWnd=CreateWindow(
		szAppName,//Имя класса окна
		L"A Basic Window the Hard Way",//Заголовок окна
		WS_OVERLAPPEDWINDOW,//Стиль окна - перекрываемое
		CW_USEDEFAULT,//Позиция на экране по умолчанию
		CW_USEDEFAULT,//левого верхнего угла как x,y...
		CW_USEDEFAULT,//Стандартный размер окна - ширина...
		CW_USEDEFAULT,//... и высота
		0,//Нет родительского окна
		0,//Нет меню
		hInstance,//Дескриптор экземпляра программы
		0//Никаких данных для создания окна
		);

	ShowWindow(hWnd,nCmdShow);//Отобразить окно
	UpdateWindow(hWnd);//Заставить перерисовывать клиентскую область окна

	//Цикл сообщений
	while(GetMessage(&msg,0,0,0)==TRUE)//Получить сообщение
	{
		TranslateMessage(&msg);//Преобразование сообщения
		DispatchMessage(&msg);//Диспетчеризация сообщения
	}

	return static_cast<int>(msg.wParam);//Конец, возврат в Windows
}

Функции обработки сообщений

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

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

Функция WindowProc()

Прототип функции WindowProc() выглядит следующим образом.

LRESULT CALLBACK WindowProc(HWND hWnd, UINT message,
                            WPARAM wParam, LPARAM lParam);

Типом возвращаемого значения LRESULT является тип, определенный операционной системой Windows, и он обычно эквивалентен типу long. Поскольку функция вызвана операционной системой Windows через указатель (вы установили этот указатель в функции WinMain(), когда заполняли структуру WNDCLASSEX), ее следует квалифицировать как CALLBACK.

Аргументы функции WindowProc()

QIP Shot - Screen 162Окно, к которому относится входящее сообщение, идентифицируется первым аргументом – hWnd, передаваемым функции. В данном случае есть только окно, потому можете проигнорировать этот аргумент.

Сообщения идентифицируются значением message, передаваемым функции WindowProc().

Декодирование сообщения Windows

Процесс декодирования сообщения, которое посылает операционная система Windows, обычно выполняется в операторе switch функции WindowProc() на основе значения message. Выбор типов сообщений, которые вы собираетесь обрабатывать, сводится к размещению оператора case для каждого из них в операторе switch. Типичная структура такого оператора switch с включением произвольных операторов case приведена ниже.

switch(message)
{
  case WM_PAINT:
    //Код рисования клиентской области
    break;
  case WM_LBUTTONDOWN:
    //Код обработки нажатия левой кнопки мыши
    break;
  case WM_LBUTTONUP:
    //Код обработки отпускания левой кнопки мыши
    break;
  case WM_DESTROY:
    //Код обработки уничтожения окна
    break;
  default:
    //Код обработки всех прочих сообщений
}

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

Рисование клиентской области окна

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

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

HDC hDC;//Деструктор контекста дисплея
PAINTSTRUCT PaintSt;//Структура, определяющая области для перерисовки
hDC=BeginPaint(hWnd,&PaintSt);//Подготовка к рисованию в окне

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

Функция BeginPrint() предоставляет в качестве возвращаемого значения контекст дисплея и требует двух аргументов. Окно в котором вы собираетесь рисовать, идентифицируется его дескриптором – hWnd, передаваемым в первом аргументе. Второй аргумент – адрес переменной PaintSt типа PAINTSTRUCT, куда операционная система Windows помещает информацию об области, подлежащей перерисовке в ответ на сообщение WM_PAINT. Координаты клиентской области можно получить в структуре RECT следующим образом.

RECT aRect;//Рабочий прямоугольник
GetClientRect(hWnd,&aRect);

Функция GetClientRect() получает координаты левого верхнего и правого нижнего углов клиентской обласит окна, указанного первым аргументом. Эти координаты сохраняются в структуре aRect типа RECT, которая передается во втором аргументе как указатель. Затем можно использовать это определение клиентской области окна, выводя в окно текст функции DrawText(). Поскольку ваше окно имеет серый фон, потребуется сделать фон выводимого текста прозрачнее; в противном случае текст появится на белом фоне. Это можно сделать следующими вызовами функций API Windows.

SetBkMode(hDC,TRANSPARENT);//Установить режим фона текста

вПервый аргумент идентифицирует контекст устройства, а второй – режим отображения фона. По умолчанию принят режим OPAQUE.

После этого можно выводить текст с помощью следующего опреатора.

DrawText(hDC,//Деструктор контекста устройства
  L"But, soft! What light through yonder window break?",
  -1,//Индикатор строки, ограниченной null
  &aRect,//Прямоугольник, в котором выполняется рисование текста
  DT_SINGLELINE|//Формат текста - одна строка
  DT_CENTER|//-центрирование в строке
  DT_VCENTER//-центрирование по высоте aRect
);

Первый аргумент функции DrawText() – ваше разрешение на рисование в окне это контекст дислея hDC. Следующий аргумент – текстовая строка, которую нужно вывести. Следующий аргумент, имеющий значение -1, указывает на то, что строка ограничена символом null. Иначе здесь нужно было укзаать количество символов строки. Четвертый аргумент – указатель на структуру RECT, определяющую прямоугольник, в котором вы хотите выводить текст. Последний аргумент определяет формат текста в прямоугольнике.

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

EndPaint(hWnd, &PaintSt);//Завершить операцию перерисовки окна

Аргумент hWnd идентифицирует окно программы, а второй аргумент является адресом структуры PAINTSTRUCT, которая была заполнена функцией BeginPaint().

Завершение программы

Вы можете предположить, что закрытие окна завершает приложение, но для того, чтобы получить такое поведение, потребуется добавить еще немного кода. Причина, по которой приложение по умолчанию не закрывается с закрытием его окна, состоит в необходимости выполнить некоторую очистку. Кроме того, может случится так, что приложение имеет несколько окон. Когда пользователь закрывает окно двойным щелчком на пиктограмме в панели заголовка или щелчком на кнопке Close (Закрыть), создается сообщение WM_DESTROY. Поэтому для закрытия приложения необходимо обработать сообщение WM_DESTROY в функции WindowProc().  Вы делаете это, создавая сообщение WM_QUIT в операторе

PostQuitMessage(0);

где аргумент – это код завершения. Данная функция API Windows делает именно то, о чем говорит ее имя, – посылает сообщение WM_QUIT в очередь сообщений вашего приложения. В результате функция GetMessage() в функции WinMain() возвращает значение FALSE и завершает цикл сообщений, завершая тем самым программу.

Мы рассмотрели все элементы, необходимые для создания полной функции WindowProc() данного примера. Ниже приведен полный код этой функции.

LRESULT CALLBACK WindowProc(HWND hWnd, UINT message,
							WPARAM wParam, LPARAM lParam)
{
	HDC hDC;//Дескриптор контекста дисплея
	PAINTSTRUCT PaintSt;//Структура, определяющая обалсть рисования
	RECT aRect;//Рабочий прямоугольник

	switch(message)//Выборочная обработка сообщений
	{
	case WN_PAINT://Сообщение для перерисовки окна
		hDC=BeginPaint(hWnd,&PaintSt);//Подготовится к
									  //перерисовке окна
		//Получить верхний левый и нижний правый углы клиентской
		GetClientRect(hWnd, &aRect);//области
		SetBkMode(hDC,TRANSPARENT);//Установить режим
								   //отображения фона текста
		//Теперь отобразить текст в клиентской области окна
		DrawText(hDC,//Дескриптор контекста устройства
			     L"But, soft! What light through younder window breaks?",
				 -1,//Индикатор строки, ограниченной null
				 &aRect,//Прямоугольник, в котором
						//выполняется рисование текста
				 DT_SINGLELINE|//Формат текста - одна строка
				 DT_CENTER|//-цетрирование в строке
				 DT_VCENTER//-центрирование по высоте
				 aRect);

		EndPaint(hWnd,&PaintSt);//Завершить операцию перерисовки
		return 0;//окна

	case WM_DESTROY://Окно уничтожается
		PostQuitMessage(0);
		return 0;

	default://Любые другие сообщения нас не интерисуют, поэтому
			//вызывается обработка сообщения по умолчанию
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
}

Все тело функции состоит из одного оператора switch. На основе идентификатора сообщения, переданного функции в параметре message, выбирается соответствующая ветвь case. Поскольку этот пример прост, необходимо обрабатывать только два сообщения – WM_PAINT и WM_DESTROY. Все прочие сообщения передаются обратно операционной системе Windows вызовом функции DefWindowProc() в части default оператора switch. Аргументы функции DefWindowProc() – те же, что переданы функции, так что вы просто передаете их обратно без изменений. Обратите внимание на наличие оператора return в конце обработки сообщения каждого типа. Для обработанных сообщений возвращается нулевое значение.

Простая программа Windows

Поскольку функции WinMain() и WindowProc() для обработки сообщений уже написаны, мы имеем все необходимое для создания полного файла исходного кода программы Windows, с использованием только интерфейса API Windows. Полный файл исходного кода состоит из директивы #include для файла заголовка windows.h, прототипа функции WindowProc, а также самих функций WinMain() и WindowProc().

//Листинг OFWIN 1
#include <Windows.h>

LRESULT WINAPI WindowProc(HWND hWnd, UINT message,
                          WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
				   LPSTR lpCmdLine, int nCmdShow)
{
	WNDCLASSEX WindowClass; //Структура для хранения артибутов окна

	static LPCTSTR szAppName=L"OFWin";//Опеределить класс окна
	HWND hWnd;//Дескриптор окна
	MSG msg;//Структура сообщения окна

	//Установить размер структуры
	WindowClass.cbSize=sizeof(WNDCLASSEX);

	//Перерисовать окно при изменении размера
	WindowClass.style=CS_HREDRAW|CS_VREDRAW;

	//Определить функцию - обработчик сообщений
	WindowClass.lpfnWndProc=WindowProc;

	WindowClass.cbClsExtra=0;//Никаких дополнительных байтов после
	WindowClass.cbWndExtra=0;//структуры класса окна в экземпляре
							 //окна

	WindowClass.hInstance=hInstance;//Дескриптор экземпляра
									//приложения

	//Установить пиктограмму приложения по умолчанию
	WindowClass.hIcon=LoadIcon(0,IDI_APPLICATION);

	//Установить стандартный курсор мыши в виде стрелочки
	WindowClass.hCursor=LoadCursor(0,IDC_ARROW);

	//Установить серую кисть для риосвания фона
	WindowClass.hbrBackground=static_cast<HBRUSH>(GetStockObject(GRAY_BRUSH));

	WindowClass.lpszMenuName=0;//Нет меню
	WindowClass.lpszClassName=szAppName;//Установить имя класса
	WindowClass.hIconSm=0;//маленькая пиктограмма по умолчанию

	//Теперь зарегистрировать класс окна
	RegisterClassEx(&WindowClass);

	//Теперь можно создать окно
	hWnd=CreateWindow(
		szAppName,//Имя класса окна
		L"A Basic Window the Hard Way",//Заголовок окна
		WS_OVERLAPPEDWINDOW,//Стиль окна - перекрываемое
		CW_USEDEFAULT,//Позиция на экране по умолчанию
		CW_USEDEFAULT,//левого верхнего угла как x,y...
		CW_USEDEFAULT,//Стандартный размер окна - ширина...
		CW_USEDEFAULT,//... и высота
		0,//Нет родительского окна
		0,//Нет меню
		hInstance,//Дескриптор экземпляра программы
		0//Никаких данных для создания окна
		);

	ShowWindow(hWnd,nCmdShow);//Отобразить окно
	UpdateWindow(hWnd);//Заставить перерисовывать клиентскую область окна

	//Цикл сообщений
	while(GetMessage(&msg,0,0,0)==TRUE)//Получить сообщение
	{
		TranslateMessage(&msg);//Преобразование сообщения
		DispatchMessage(&msg);//Диспетчеризация сообщения
	}

	return static_cast<int>(msg.wParam);//Конец, возврат в Windows
}

//Листинг OFWIN_2
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message,
							WPARAM wParam, LPARAM lParam)
{
	HDC hDC;//Дескриптор контекста дисплея
	PAINTSTRUCT PaintSt;//Структура, определяющая обалсть рисования
	RECT aRect;//Рабочий прямоугольник

	switch(message)//Выборочная обработка сообщений
	{
	case WM_PAINT://Сообщение для перерисовки окна
		hDC=BeginPaint(hWnd,&PaintSt);//Подготовится к
									  //перерисовке окна
		//Получить верхний левый и нижний правый углы клиентской
		GetClientRect(hWnd, &aRect);//области
		SetBkMode(hDC,TRANSPARENT);//Установить режим
								   //отображения фона текста
		//Теперь отобразить текст в клиентской области окна
		DrawText(hDC,//Дескриптор контекста устройства
			     L"But, soft! What light through younder window breaks?",
				 -1,//Индикатор строки, ограниченной null
				 &aRect,//Прямоугольник, в котором
						//выполняется рисование текста
				 DT_SINGLELINE|//Формат текста - одна строка
				 DT_CENTER|//-цетрирование в строке
				 DT_VCENTER//-центрирование по высоте
				 );

		EndPaint(hWnd,&PaintSt);//Завершить операцию перерисовки
		return 0;//окна

	case WM_DESTROY://Окно уничтожается
		PostQuitMessage(0);
		return 0;

	default://Любые другие сообщения нас не интерисуют, поэтому
			//вызывается обработка сообщения по умолчанию
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
}

вывод:QIP Shot - Screen 163

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

Системное меню и кнопка Close (Закрыть) – это также стандартные принадлежности вашего окна, поскольку были указаны соответствующие параметры в структуре WindowClass. Опять таки операционная система Windows берет управление на себя. Единственное дополнительное действие в вашей программе происходит при отправке сообщения WM_DESTROY в момент закрытия окна, как было описано ранее.

Огранизация программ Windows

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

Это может быть не совсем очевидно на основе показанного кода, но такая структура является сердцем всех программ Windows, включая программы среды CLR. Понимание организации приложений Windows часто может помочь, когда вы попытаетесь разобраться почему в приложении что-то работает не так, как должно. Функция WinMain() вызывается операционной системой Windows в начале выполнения программы, а функция WindowProc(), которая иногда называется WndProc(), вызывается операционной системой при каждой передаче сообщения окну приложения. Обычно в приложении имеется отдельная функция WindowProc() для каждого окна приложения.

QIP Shot - Screen 164

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

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

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

Библиотека Microsoft Foundation Classes

Библиотека Microsoft Foundation Classes (MFC) – это набор предопределенных классов, на которых построено программирование для Windows в среде разработки Visual C++. Эти классы представляют объективно-ориентированный подход к программированию Windows, инкапсулирующий интерфейс API Windows. Библиотека MFC не следует строго объектно-ориентированным принципам в части инкапсуляции и сокрытия данных-главным образом потому, что большая часть кода библиотеки MFC была написана до того, как эти принципы были установлены.о

Процесс написания программы Windows включает создание и использование объектов библиотеки MFC либо объектов классов, унаследованных от них. В основном вы наследуете собственные классы от классов библиотеки MFC с существенной помощью со стороны специализированных инструментов среды разработки Visual C++ 2010, что упрощает задачу. Объекты этих основанных на классах библиотеки MFC типов классов включают функции-члены для взаимодействия с Windows, обработки сообщений Windows, а также отправки сообщений друг другу. Эти производные классы, конечно же наследуют все члены своих базовых классов. Унаследованные функции выполняют практически всю черновую работу, обеспечивающую работу приложения Window. Все, что необходимо сделать, – добавитть данные и функции-члены для настройки поведения классов и обеспечения специфичной для приложения функциональности, которая требуется от вашей программы.

Нотация библиотеки MFC

Все классы библиотеки MFC имеют имена, начинающиеся с “С”, вроде CDocument или CView. Если вы используете те же соглашения, определяя собственные классы или наследуя их от классов из библиотеки MFC, то ваши программы будут легче восприниматься. Переменные-члены классов библиотеки MFC снабжены префиксом m_. Я также буду следовать этому соглашению в примерах, использующих библиотеку MFC.

Вы обнаружите, что в библиотеке MFC применяется венгерская нотация для многих имен переменных – в частности тех, что происходят от интерфейса API Windows. Как вы помните, это предусматривает префикс p для указателей, n-для int; 1-для long; h-для дескрипторов и т.д.

Структуризация программы MFC

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

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

Минимальное приложение MFC

Создаем новый проект, выбрав пункт меню File=>New=>Project (Файл=>Создать=>Проект). Здесь мы не будем использовать мастер создания приложений, выдающий базовый код, поэтому укажем в качестве шаблона проекта Win32Project (Проект Win32) и выберите параметры Windows Application (Приложение Windows) и Empty project (Пустой проект) в следующем диалоговом окне. После того как проект будет создан, выберите пункт Project=>properties (Проект=>Свойства) из главного меню и на вкладке General (Общие) окна Configuration Properties (Свойства конфигурации) щелкните на свойстве Use of MFC (Использовать MFC), чтобы установить значение Use MFC в Shared DLL (Совместно используемая DLL).

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

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

#include <afxwin.h>//Для библиотеки классов

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

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

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

class COurApp : public CWinApp
{
   public:
      virtual BOOL InitInstance();
};

Здесь нет никакой особой специализации. Мы включили только один член в определение класса – функцию InitInstance(). Эта функция определена в базовом классе как виртуальная, т.е. она не является новой в нашем производном классе; мы просто переопределяем функцию базового класса приложения. Все прочие данные и функции-члены, которые понадобятся, наследуются от класса CWinApp без изменений.

Класс окна

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

class COurWnd : public CFrameWnd
{
   public:
      //Конструктор
      COurWnd()
      {
         Create(0,L"Our Dumb MFC Application");
      }
};

Функция Create(), которая вызывается в конструкторе, унаследована от базового класса. Она создает окно и присоединяет его к создаваемому объекту COurWnd.

Обратите внимание на то, что объект COurWnd-это не то же самое, что окно, отображаемое операционной системой Windows, – объект класса и физическое окно представляют собой разные вещи.

Завершение программы

Имея определенный класс окна для приложения, напишем функцию InitInstance() класс COurApp.

BOOL COurApp::InitInstance(void)
{
   //Создать объект окна в динамической памяти
   m_pMainWnd=new COurWnd;
   m_pMainWnd->ShowWindow(m_nCmdShow);//... и отобразить его
   return TRUE;
}

Это переопределяет виртуальную функцию, определенную в базовом классе CWinApp, и как уже упоминалось, эта функция вызывается из функции WinMain(), автоматически предоставленной библиотекой MFC. Функция InitInstance() создает объект главного окна приложения в динамической памяти с помощью оператора new. Вы сохраняете возвращенный адрес в переменной m_pMainWnd, которая является унаследованным членом класса COurApp. В результате объект приложения владеет объектом окна. Вам даже не нужно беспокоиться об освобождении памяти созданного объекта – функции WinMain() позаботится о необходимой очистке.

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

COurApp AnApplication;//Определение объекта приложения

Готовый продукт

#include <afxwin.h> //Для библиотеки классов

//Определение класса приложения
class COurApp:public CWinApp
{
public:
	virtual BOOL InitInstance();
};

//Определение класса окна
class COurWnd:public CFrameWnd
{
public:
	//Конструктор
	COurWnd()
	{
		Create(0,L"Our Dumb MFC Aplication");
	}
};

//Функция создания экземпляра главного окна приложения
BOOL COurApp::InitInstance(void)
{
	//Построить объект окна в динамической памяти
	m_pMainWnd=new COurWnd;
	m_pMainWnd->ShowWindow(m_nCmdShow);//... и отобразить его
	return TRUE;
}

//Определение объекта приложения в глобальном контексте
COurApp AnApplication;//Определение объекта приложения

вывод:QIP Shot - Screen 165

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

Размер окна можно изменять, перетаскивая его границу, можно перемещать само окно по экрану, сворачивать и разворачивать обычным образом. Единственная дополнительная функция, которую поддерживает эта программа – это “закрытие”. для чего можно использовать системное меню, кнопка Close в верхнем правом углу окна или комбинацию клавиш <Alt+F4>. Не так много, но с учетом того, насколько мало строк кода понадобилось для этого, – достаточно впечатляюще.

Использование Windows Forms

Форма Windows (Windows Form) – это окно некоторого рода. Под окном подразумевается окно в его самом общем смысле – область экрана, которая может быть кнопкой, диалоговым окном. обычным окном или видимым компонентом пользовательского интерфейса любого типа. Форма Windows инкапсулируется классами, производными от класса System::Windows::Farms, но вы поначалу не должны особенно беспокоится об этом, поскольку весь код, необходимый для создания формы, создается автоматически. Чтобы увидеть насколько это просто, создайте базовое окно, используя Windows Forms со стандартным меню.

Выберите в качестве типа проект CLR в диалоговом окне New Project (Новый проект) и установите Windows Forms Application (Приложение Windows Forms) в качестве шаблона проекта. Введите имя проекта test. После щелчка на кнопке Ок мастер приложений Application Wizard создаст код для формы приложения Windows и отобразит окно проекта, содержащее форму, отображаемую приложением

QIP Shot - Screen 166

Теперь в панели конструктора можно графически изменять форму, и эти изменения автоматически отразятся в коде создания формы. Для начала перетащите нижний угол формы, чтобы увеличить размер ее окна. Или измените текст в панели заголовка – для этого щелкните правой кнопкой мыши на клиентской области формы и выберите в контекстном меню пункт Properties (Свойства). Открывшееся окно Properties (Свойства) позволит изменять свойства формы. В списке свойств справа от панели конструктора выберите Text (Текст), а затем введите новый текст панели заголовка в соседней колонке, показывающей значение свойства. (Я ввел A Simple Form Window.) После нажатия клавиши <Enter> новый текст появится в панели заголовка формы.

Для того чтобы убедиться, насколько просто добавлять что-либо в окно формы, отобразите панель инструментов, выбрав вкладку в правой части окна, если оно отображается, либо нажав комбинацию клавиш <Ctrl+Alt+X>, либо выбрав пункт Toolbox (Панель инструментов) в меню View(Вид). Найдите элемент MenuStrip (Полоса меню) в списке и перетащите его в окно формы на панели вкладки конструктора. Щелкните правой кнопкой мыши на элементе MenuStrip1, который появится под окном формы, и выберите в контекстном меню пункт Insert Standard Items (Добавить стандартные элементы). В окне формы отобразиться панель меню, наполненная стандартными меню File (Файл), Edit (Правка), Tools (Сервис), Help (Справка) со своими раскрывающимися списками элементов.

QIP Shot - Screen 167

Подвод итогов

В этой статье были продемонстрированы три способа создания элементарного приложения Windows в среде Visual C++ 2010 и рассмотрены существенные различия между ними.

На этом пожалуй и все.

[youtube]https://www.youtube.com/watch?v=DnfQMwhjjFs[/youtube]

Комментарии:


Оставить комментарий

Your email address will not be published. Required fields are marked *