Лабораторная работа № Часть Основы создания графических приложений в системе Windows. 1 Работа с окнами 1



страница1/3
Дата02.12.2016
Размер1 Mb.
Просмотров342
Скачиваний0
ТипЛабораторная работа
  1   2   3

Оглавление


Лабораторная работа № 1. Часть 1. Основы создания графических приложений в системе Windows. 1

Работа с окнами 1

Окно рабочего стола (Desktop Window) 1

Окна приложений 1

Создание простейшего оконного приложения 7

Создание главного окна приложения 13

Создание главного окна приложения при помощи MFC 17

Создание главного окна приложения при помощи библиотеки WTL 20

Основы вывод графической информации в системе Windows 24

Контекст устройства 25

Сообщение WM_PAINT 25

Работа с объектами GDI 28

Использование таймера для создания анимированных изображений 33

Практические задания 40

Обязательные задания 40

Дополнительные задания 41




Лабораторная работа № 1. Часть 1. Основы создания графических приложений в системе Windows.

Работа с окнами


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

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

С каждым окном в системе Windows связан дескриптор окна – специальное значение типа HWND, которое уникально идентифицирует окно в системе. После создания окна приложение получает его дескриптор и может управлять окном, передавая дескриптор окна в соответствующие функции управления окнами.

Окно рабочего стола (Desktop Window)


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

Окна приложений


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

На следующем рисунке показаны типичные компоненты главного окна приложения:



typical window

Клиентская и неклиентская область окна


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

Элементы управления и диалоговые окна


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

Элементы управления часто используются совместно с диалоговыми окнами (Dialog Boxes) – окна, содержащие один или больше элемент управления. Как правило, диалоговые окна не содержат полос прокрутки, кнопок максимизации и минимизации, меню, а также не позволяют выполнять изменение своего размера:





Окна сообщений (Message Boxes) – специальный тип диалоговых окон, используемый для вывода сообщений, предупреждений или сообщений об ошибках:


Классы окон


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

Прежде чем создавать окна, приложение должно зарегистрировать в системе соответствующий класс окна. Для этого необходимо заполнить структуру WNDCLASSEX, объявленную следующим образом:

typedef struct {

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

UINT style; // стили класса

WNDPROC lpfnWndProc;// адрес оконной процедуры

int cbClsExtra; // количество дополительных байт, занимаемых классом

int cbWndExtra; // количество дополнительных байт, занимаемых окном

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

HICON hIcon; // дескриптор большой иконки окна

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

HBRUSH hbrBackground; // дескриптор кисти, используемой для заливки фона LPCTSTR lpszMenuName; // имя меню (в ресурсах приложения)

LPCTSTR lpszClassName; // имя класса окна

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

} WNDCLASSEX, *PWNDCLASSEX;

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

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

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

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

Дескриптор кисти определяет способ заливки клиентской области окна

Имя меню – имя ресурса2 меню, используемого окнами данного класса

Имя класса окна – имя, под которым будет зарегистрирован данный класс окна. При создании нового окна приложение указывает имя класса окна, а также дескриптор экземпляра приложения, в котором данный класс был объявлен.

После заполнения полей структуры WNDCLASSEX, приложение должно вызвать системную функцию RegisterClassEx, передав ей адрес данной структуры.


Сообщения и очередь сообщений

Оконная процедура

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

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

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

Оконная процедура объявляется следующим образом:

LRESULT CALLBACK WindowProc(

HWND hwnd, // дескриптор окна

UINT uMsg, // идентификатор сообщения

WPARAM wParam, // дополнительный параметр 1

LPARAM lParam); // дополнительный параметр 2

Квалификатор CALLBACK задает способ вызова функции3, используемый ОС Windows для осуществления вызовов пользовательских процедур.

Дескриптор окна используется системой и приложением для идентификации окна, которому предназначается сообщение.

Идентификатор сообщения – именованная целочисленная константа, идентифицирующая цель сообщения. Оконная процедура использует идентификатор сообщения для того, чтобы определить, каким образом следует обработать полученное сообщение. Например, сообщение с идентификатором WM_PAINT сообщает приложению о том, что клиентская область окна или ее часть нуждается в перерисовке.

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

Возвращаемое значение имеет целочисленный тип LRESULT и зависит от того, какое сообщение было обработано.

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

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

Кроме того, одна и та же оконная процедура используется для обработки сообщений всех окон одного и того же класса. В случае, когда одновременно в программе могут существовать несколько окон одного и того же класса, следует исключить хранение специфичных для окна данных в области глобальных переменных. Решить данную проблему может применение ООП, когда для каждого окна приложения создается соответствующий экземпляр C++-класса, данные которого хранят специфичную для экземпляра окна информацию, а методы занимаются обработкой сообщений. В этом случае оконная процедура должна по дескриптору окна-адресата сообщения определить соответствующий ему экземпляр C++-класса обработчика и перенаправить обработку сообщения данному обработчику. Для этих целей можно использовать, например, класс std::map.

Использование готовых фреймворков, вроде MFC, WTL, Qt, wxWidgets и др., может также существенно облегчить процесс создания графических приложений для системы Windows на C++.

Оконная процедура по умолчанию

Функция операционной системы DefWindowProc определяет некоторые фундаментальные аспекты поведения, свойственные всем окнам, обеспечивая минимально необходимый функционал окна.

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


Очередь обработки сообщений

В Windows используется два способа маршрутизации сообщений – помещение сообщения в очередь сообщений4 и отправка сообщений напрямую в оконную процедуру.

Сообщения попадают в очередь сообщений, главным образом, в результате действий пользователя (WM_MOUSEMOVE, WM_LBUTTONDOWN, WM_KEYDOWN и другие). Сообщения WM_TIMER, WM_PAINT и WM_QUIT также всегда помещаются в очередь сообщений.

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

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


Цикл выборки сообщений (Message Loop)

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

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

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

Простейший цикл выборки сообщений выглядит следующим образом:

MSG msg;

BOOL res;

while ((res = GetMessage(&msg, NULL, 0, 0)) != 0)

{

if (res == -1)



{

// произошла ошибка - нужно обработать ее и, вероятно,

// завершить работу приложения

}

else



{

// Если это сообщение о нажатии виртуальной клавиши,

// то добавляем в очередь сообщений сообщения, несущие информацию о

// коде вводимого пользователем символа

TranslateMessage(&msg);

// передаем сообщение в соответствующую оконную процедуру

DispatchMessage(&msg);

}

}



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

Создание простейшего оконного приложения


Рассмотрим процесс создания оконного приложения с помощью Microsoft Visual C++.

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



В окне настройки приложения выберем тип приложения Windows application. Чтобы продемонстрировать процесс создания приложения с нуля, установим флажок Empty project.



Т.к. созданный проект не содержит никаких файлов, добавим новый файл main.cpp к проекту:




Функция WinMain - точка входа в приложение


Если точкой входа в консольное приложение на C/C++ является функция main(), то для графических приложений Windows такой функцией является функция WinMain.

Прототип данной функции описан ниже:

int WINAPI WinMain(

HINSTANCE hInstance,

HINSTANCE hPrevInstance,

LPSTR lpCmdLine,

int nCmdShow);

Параметр hInstance (handle of the instance) задает дескриптор текущего экземпляра приложения. Важно сохранить значение данного параметра в переменной, т.к. он нам понадобится для создания окна.

Параметр hPrevInstance использовался в ранних версиях Windows для определения факта предыдущего запуска приложений. В настоящее время он не используется и всегда равен NULL. Его можно смело игнорировать.

Параметр lpCmdLine хранит командную строку приложения (за исключением имени программы).

Параметр nCmdShow определяет, каким образом должно быть показано главное окно приложения (развернутое на весь экран, минимизированное, или обычное) – данный режим обычно задается в свойствах ярлыка для запуска программы. Разумеется, разработчика приложения никто не обязывает принимать во внимание данный параметр, хотя это является правилом хорошего тона.

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

Подробно, параметры функции WinMain описаны в MSDN:

http://msdn.microsoft.com/en-us/library/ms633559%28VS.85%29.aspx

Чтобы компилятору стало известно о новых типах аргументов HINSTANCE, LPTSTR, необходимо подключить заголовочный файл windows.h.

Программа “Hello World”


Воспользуемся функцией MessageBox для вывода классического сообщения «Hello world»:

#include

#include
int WINAPI WinMain(

HINSTANCE /*hInstance*/,

HINSTANCE /*hPrevInstance*/,

LPSTR /*lpCmdLine*/,

int /*nCmdShow*/)

{

MessageBox(NULL, _T("Hello world"), _T("My first windows program"), MB_OK);



return 0;

}

При помощи функции MessageBox можно вывести стандартное окно сообщения. В качестве первого параметра задается дескриптор родительского окна, либо NULL, если в качестве родительского окна должно быть окно рабочего стола. Другие параметры задают текст сообщения, заголовок окна, а также управляют наличием кнопок и иконок в окне.



Функция MessageBox не возвращает управление до тех пор, пока пользователь не нажмет на одну из кнопок в окне. Возвращенное функцией целочисленное значение может использоваться приложением для определения нажатой кнопки. Например:

#include

#include


int WINAPI WinMain(

HINSTANCE /*hInstance*/,

HINSTANCE /*hPrevInstance*/,

LPSTR /*lpCmdLine*/,

int /*nCmdShow*/)

{

if (MessageBox(NULL, _T("Do you like to study?"),



_T("Yes/no example"), MB_YESNO | MB_ICONQUESTION) == IDYES)

{

MessageBox(NULL, _T("Good boy!"), _T("Wow"), MB_OK);



}

else // IDNO

{

MessageBox(NULL, _T("You are so lazy!"), _T("I'm so sad"), MB_OK);



}

return 0;

}


Пару слов о Unicode6


Следует отметить, что фактическое значение типа LPTSTR (а также других типов, так или иначе связанных со строками и символами в Windows), зависят от того, в каком режиме было скомпилировано приложение. В случае, если приложение было скомпилировано как поддерживающее Unicode, тип LPTSTR превращается в LPWSTR (каждый символ в такой строке является типом SHORT), в противном случае (ANSI-приложение) – LPSTR (каждый символ в такой строке является типом CHAR).

Операционные системы Windows семейства NT (Windows NT, 2000, и выше) поддерживают Unicode. В то же время, для совместимости с приложениями, написанными для Windows 95, 98, ME, в них остались ANSI-версии многих функций, работающих со строками. В частности, существуют две версии функции MessageBox: MessageBoxA и MessageBoxW, принимающие, соответственно, ANSI и Unicode-строки. Строго говоря, MessageBox – это не функция, а макрос, который в зависимости от определения макроса UNICODE, разворачивается в ту или иную версию функции.

Макрос _T позволяет задавать строковые и символьные литералы в формате Unicode, либо Multi-byte. Например, строка
TCHAR message[] = _T("Hello");
будет развернута в
CHAR message[] = "Hello";
либо в
SHORT message[] = L"Hello";
в зависимости от наличия макроса UNICODE.

Для использования макроса _T необходимо подключить заголовочный файл tchar.h

Заголовочные файлы Windows содержат множество конструкций вида (файл WinUser.h):

WINUSERAPI

int

WINAPI


MessageBoxA(

__in_opt HWND hWnd,

__in_opt LPCSTR lpText,

__in_opt LPCSTR lpCaption,

__in UINT uType);

WINUSERAPI

int

WINAPI


MessageBoxW(

__in_opt HWND hWnd,

__in_opt LPCWSTR lpText,

__in_opt LPCWSTR lpCaption,

__in UINT uType);

#ifdef UNICODE

#define MessageBox MessageBoxW

#else


#define MessageBox MessageBoxA

#endif // !UNICODE

Задать тип используемого приложением набора символов (в частности, включить или выключить определение макроса UNICODE при компиляции) - Unicode или Multibyte (ANSI) – можно в свойствах проекта в разделе Configuration PropertiesGeneral.

Следует отметить, что эта настройка, как правило, оказывает влияние лишь на заголовочные файлы Windows. Другие библиотеки (в частности, STL, boost) их не учитывают



Какой тип приложения использовать (Unicode или нет) программист решает самостоятельно, однако в 21 веке каждое уважающее себя Windows-приложение должно поддерживать Unicode.


Создание главного окна приложения


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

Шаг 1 – Регистрируем класс главного окна приложения


Для регистрации класса окна заполним структуру WNDCLASSEX необходимыми данными и при помощи RegisterClassEx зарегистрируем класс главного окна нашего приложения. Заметим, что здесь как раз нам и пригодится параметр hInstance, передаваемый в функцию WinMain.
TCHAR const CLASS_NAME[] = _T("MainWndClass");
LRESULT CALLBACK WindowProc

(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);


bool RegisterWndClass(HINSTANCE hInstance)

{

WNDCLASSEX wndClass =



{

sizeof(wndClass), //UINT cbSize;

CS_HREDRAW | CS_VREDRAW, //UINT style;

&WindowProc, //WNDPROC lpfnWndProc;

0, //int cbClsExtra;

0, //int cbWndExtra;

hInstance, //HINSTANCE hInstance;

NULL, //HICON hIcon;

LoadCursor(NULL, IDC_ARROW), //HCURSOR hCursor;

(HBRUSH)(COLOR_BTNFACE + 1), //HBRUSH hbrBackground;

NULL, //LPCTSTR lpszMenuName;

CLASS_NAME, //LPCTSTR lpszClassName;

NULL, //HICON hIconSm;

};
return RegisterClassEx(&wndClass) != FALSE;

}

Стили класса окна CS_HREDRAW и CS_VREDRAW означают, что горизонтальное и вертикальное размеров окна должно приводить к очистке всего окна, а не только его части.



Функцию RegisterWndClass вызовем из функции WinMain:

int WINAPI WinMain(

HINSTANCE hInstance,

HINSTANCE /*hPrevInstance*/,

LPSTR /*lpCmdLine*/,

int /*nCmdShow*/)

{

if (!RegisterWndClass(hInstance))



{

return 1;

}

return 0;



}

Шаг 2 – Создаем оконную процедуру


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

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

LRESULT CALLBACK WindowProc(

HWND hwnd,

UINT uMsg,

WPARAM wParam,

LPARAM lParam)

{

switch (uMsg)



{

case WM_DESTROY:

// Помещаем сообщение WM_QUIT в очередь сообщений текущего потока

PostQuitMessage(0);

break;

default:



return DefWindowProc(hwnd, uMsg, wParam, lParam);

}

return 0;



}

Наш обработчик сообщения WM_DESTROY помещает в очередь сообщений текущего потока сообщение WM_QUIT. Данное сообщение будет являться признаком завершения цикла выборки сообщений.


Шаг 3 – Создаем главное окно приложения


После того, как класс главного окна приложения зарегистрирован, можно приступить к созданию окон на его основе. Для этого следует воспользоваться функцией7 CreateWindowEx.

HWND WINAPI CreateWindowEx(

DWORD dwExStyle, // расширенные стили окна

LPCTSTR lpClassName, // имя класса окна

LPCTSTR lpWindowName, // имя окна (заголовок)

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

int x, // координата X верхнего левого угла окна

int y, // координата Y верхнего левого угла окна

int nWidth, // ширина окна (в пикселях)

int nHeight, // высота окна (в пикселях)

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

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

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

LPVOID lpParam // параметр, передаваемый окну при его создании

);

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



Для окон, содержащих строку заголовка, параметр «имя окна» задает текст, отображаемый в строке заголовка окна. Если функция CreateWindowEx используется для создания окон элементов управления (кнопки, флажки, радио-кнопки, надписи), данный параметр указывает текст, отображаемый элементом управления.

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

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

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

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

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

Параметр lpParam позволяет передать создаваемому окну дополнительную информацию, например, данные связанные с окном. Данный параметр используется также при создании окон в приложениях с многодокументным интерфейсом (MDI-приложения).

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

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

TCHAR const CLASS_NAME[] = _T("MainWndClass");

TCHAR const WINDOW_TITLE[] = _T("My first window");

...


HWND CreateMainWindow(HINSTANCE hInstance)

{

HWND hMainWindow = CreateWindowEx(



0, // расширенные стили окна

CLASS_NAME,

WINDOW_TITLE,

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

CW_USEDEFAULT, CW_USEDEFAULT, // координаты по-умолчанию

CW_USEDEFAULT, CW_USEDEFAULT, // размер по умолчанию

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

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

hInstance,

NULL); // доп. параметры окна


return hMainWindow;

}

Шаг 4 – Показываем главное окно


Окно мы создали, но окно создается изначально скрытым. Окно можно было бы создать видимым изначально, добавив флаг WS_VISIBLE к флагу WS_OVERLAPPEDWINDOW при помощи операции побитовой операции | (ИЛИ). В нашем случае мы это сделали специально, чтобы продемонстрировать использование параметра nCmdShow, передаваемого в WinMain. При помощи функций ShowWindow и UpdateWindow выполним отображение окна и обновление его содержимого.

ShowWindow(hMainWindow, nCmdShow);

UpdateWindow(hMainWindow);

Шаг 5 – Запускаем цикл выборки сообщений


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

int MainLoop()

{

MSG msg;



BOOL res;

while ((res = GetMessage(&msg, NULL, 0, 0)) != 0)

{

if (res == -1)



{

// произошла ошибка - нужно обработать ее и, вероятно,

// завершить работу приложения

}

else



{

// Если это сообщение о нажатии виртуальной клавиши,

// то добавляем в очередь сообщений сообщения, несущие информацию о

// коде вводимого пользователем символа

TranslateMessage(&msg);

// передаем сообщение в соответствующую оконную процедуру

DispatchMessage(&msg);

}

}


// сюда мы попадем только в том случае извлечения сообщения WM_QUIT

// msg.wParam содержит код возврата, помещенный при помощи функции

// PostQuitMessage()

return msg.wParam;

}

Шаг 6 – Собираем все функции воедино


Теперь, когда все подготовительные шаги выполнены, приведем окончательный вариант функции WinMain:

int WINAPI WinMain(

HINSTANCE hInstance,

HINSTANCE /*hPrevInstance*/,

LPSTR /*lpCmdLine*/,

int nCmdShow)

{

// Регистрируем класс главного окна



if (!RegisterWndClass(hInstance))

{

return 1;



}
// Создаем главное окно приложения

HWND hMainWindow = CreateMainWindow(hInstance);

if (hMainWindow == NULL)

{

return 1;



}

// Показываем главное окно приложения

ShowWindow(hMainWindow, nCmdShow);

UpdateWindow(hMainWindow);


// Запускаем цикл выборки сообщений, пока не получим

// сигнал о завершении приложения

return MainLoop();

}

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


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

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

Наша оконная процедура обрабатывает только сообщение WM_DESTROY, которое посылается окну непосредственно перед его разрушением. Разрушение окна происходит после его закрытия, либо может быть осуществлено программистом явно при помощи функции DestroyWindow(). Для того, чтобы известить приложение о необходимости завершения работы, обработчик сообщения WM_DESTROY использует функцию PostQuitMessage(), которая помещает в очередь сообщений текущего потока сообщение WM_QUIT.

После создания главного окна приложения мы делаем его видимым, используя параметр nCmdShow, переданный в функцию WinMain.

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

Создание главного окна приложения при помощи MFC


Библиотека классов MFC (Microsoft Foundation Classes), поставляемая в составе Microsoft Visual Studio, содержит широкий спектр классов и функций, значительно упрощающих процесс создания графических приложений.

Создайте новый проект, а в качестве типа проекта укажите MFC Application:



На вкладке Application type мастера создания проекта выберите Single Document без поддержки архитектуры Document-View:



На странице настройки особенностей пользовательского интерфейса оставим настройки без изменений:



На странице настроек для продвинутых пользователей оставим только галочку Common Control Manifest, что позволит нашему приложению использовать визуальные стили окон и элементов управления, появившихся начиная с Windows XP.



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



  • CMainFrame – класс главного окна приложения. Содержит строку состояния, меню, панель инструментов, а также видовое окно.

  • CChildView – класс видового окна. Используется для вывода информации в окне.

  • CAboutDialog – диалоговое окно «О программе».

  • CMyApp – класс приложения. Используется для инициализации приложения и создания окон.


Создание главного окна приложения при помощи библиотеки WTL


Windows Template Library (WTL) – свободно распространяемая библиотека шаблонов C++, предназначенная для создания стандартных GUI приложений Windows, являющаяся расширением библиотеки ATL. Данная библиотека представляет собой объектно-ориентированную обертку над интерфейсом Win32 API, и в первую очередь разрабатывалась как облегченная альтернатива библиотеке MFC.

Установка WTL


Текущей на данный момент версией WTL является WTL 8.1. Исходный код библиотеки доступен по адресу http://sourceforge.net/projects/wtl/files/

Для использования совместно с Visual Studio 2008 рекомендуется установить самую последнюю версию библиотеки. Для этого нужно скачать архив библиотеки и распаковать его содержимое, например, в папку c:\sdk\wtl81:



Для установки мастера создания WLT-приложений для Visual Studio, необходимо запустить из директории AppWiz, один из .js файлов. Для Visual Studio 2008 это файл setup90.js. Для этого зайдите в данный каталог при помощи утилиты cmd.exe и выполните команду



wscript.exe setup90.js

Если по каким-то причинам при запуске .js файла вы получите сообщение об ошибке, то решение найти можно по следующей ссылке



http://tinyurl.com/fix-js-extension

После этого необходимо добавить каталог {ПУТЬ-УСТАНОВКИ-WTL}\Include в раздел Include Files настроек Visual C++ (Tools->Options->Projects and Solutions->VC++ Directories):



На этом установку WTL можно считать завершенной.


Создаем графическое приложение при помощи WTL Application Wizard


Добавьте новый проект, воспользовавшись меню ATL/WTL Application Wizard:

В разделе Application Type выберите SDI Application (приложение для редактирования одного документа), а также установите флажок Generate .CPP Files (для того, чтобы мастер создания нового документа расположил реализацию методов генерируемых классов в .cpp файлах, а не в .h).



На странице User Interface Features можно задать дополнительные возможности, предоставляемые главным окном приложения:



После этого будет сгенерирован каркас графического приложения, содержащего следующие классы:



  • CMainFrame – класс главного окна приложения

  • CApplicationView – класс видового окна

  • CAboutDlg – класс диалогового окна с информацией о программе.




Поделитесь с Вашими друзьями:
  1   2   3


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

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


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