Н. А. Литвиненко


Некоторые комментарии к стандартной заготовке



Pdf просмотр
страница3/15
Дата28.11.2016
Размер8.29 Mb.
Просмотров3179
Скачиваний1
1   2   3   4   5   6   7   8   9   ...   15
Некоторые комментарии к стандартной заготовке

Мастер создает четыре файла включений к стандартной заготовке, а также два файла реализации. Это сделано в целях сокращения времени компиляции прило- жения. Дело в том, что при внесении изменений в проект происходит лишь частич- ная компиляция измененных файлов. Поэтому основные библиотеки подключают- ся файлом stdafx.cpp
. В файлах включений stdafx.h и
targetver.h размещены основные определения и ключи компиляции, а в файле resource.h
— определения символических констант.
Рассмотрим подробнее файл standard.cpp
Следует обратить внимание на первые две строки головной функции
_tWinMain()
:
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
Эти определения служат лишь указанием компилятору, что параметры hPrevInstance и lpCmdLine не используются и не стоит обращать на них внимания.

Глава 1

20
Текстовые строки с именем окна и класса окна, совпадающие по умолчанию с име- нем проекта, размещаются в ресурсе приложения и загружаются функцией
LoadString()
. Для редактирования ресурсов приложения используется редактор ресурсов, о нем мы поговорим позднее. Сейчас лишь отметим то, что, при загрузке приложения в память ресурсы загружаются после кода и могут быть извлечены при помощи соответствующего набора функций.
В функции
MyRegisterClass() после заполнения полей структуры
WNDCLASSEX
происходит регистрация класса окна
RegisterClassEx()
Макрос
MAKEINTRESOURCE()
:
#define MAKEINTRESOURCEA(i) ((LPSTR)((ULONG_PTR)((WORD)(i))))
#define MAKEINTRESOURCEW(i) ((LPWSTR)((ULONG_PTR)((WORD)(i))))
#ifdef UNICODE
#define MAKEINTRESOURCE MAKEINTRESOURCEW
#else
#define MAKEINTRESOURCE MAKEINTRESOURCEA
#endif // !UNICODE используется для приведения идентификатора ресурса к типу, необходимому функции
LoadIcon()
Обратите внимание, что здесь используются расширенные версии структуры клас- са окна и API-функций, на это указывает суффикс "
Ex
", они отличаются от исход- ных версий лишь дополнительным полем, которое заполняется в данном случае по умолчанию.
Создание окна выделено в отдельную функцию
InitInstance()
. Обратите внима- ние, что дескриптор приложения сохранили на глобальном уровне в переменной hInst
. Окно создается так же, как и в листинге 1.1, но с проверкой — удачно ли оно создано? Если обращение к функции
CreateWindow() завершилось неудачей, возвращается 0 и работа приложения будет завершена. Отображается окно функци- ей
ShowWindow()
с текущим режимом отображения nCmdShow
Здесь появилась функция
UpdateWindow()
, которая проверяет — прорисовалось ли окно? Если окно отображено, функция ничего не делает, иначе функция ждет, пока окно не будет прорисовано. В большинстве случаев можно обойтись без этой функции.
После того как окно будет отображено, происходит загрузка таблицы клавиш- акселераторов "горячих клавиш", которые создаются в редакторе ресурсов для бы- строго обращения к меню приложения. Загрузка их из ресурса приложения осуще- ствляется функцией
LoadAccelerators()
В цикле обработки сообщений появилась строка: if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
, которая проверяет, не является ли сообщение результатом нажатия на "горячую клавишу"? Если это так, происходит генерация сообщения
WM_COMMAND
, как и для соответствующего пункта меню, иначе обработка сообщения происходит стан- дартным способом.


Интерфейс
Windows-
приложения

21
Обратите также внимание, что прекращение работы приложения происходит с воз- вращаемым значением
(int)msg.wParam
. Если приложение завершается обычным образом после сообщения
WM_QUIT
, то это
0
, однако здесь появляется возможность изменить код возвращаемого значения при аварийном завершении приложения.
Рассмотрим сейчас оконную функцию
WndProc()
. Две переменные wmId и wmEvent типа int предназначены для хранения дополнительной информации из младшего и старшего слова wParam
. Для извлечения их значений используются макросы
LOWORD() и
HIWORD()
. О том, как обрабатываются сообщения при выборе пункта меню, мы поговорим позднее.
Еще две переменные:
PAINTSTRUCT ps;
HDC hdc; необходимы для вывода в окно при обработке сообщения
WM_PAINT
. Вывод в окно мы обсудим при рассмотрении следующей задачи в листинге 1.3.
Функция
About() нужна для обработки пункта меню "О программе". Оставим обсуждение этой функции до главы 3.
Обработка сообщений

Операционная система способна генерировать сообщения с номерами до 0х400, номера же от 0х400 и далее зарезервированы для пользовательских сообщений.
В файле включений winuser.h размещены макроимена системных сообщений.
Этот файл вызывается неявно через файл windows.h
. Рассмотрим технику обработ- ки наиболее распространенных сообщений Windows.
Нажатие клавиши

При нажатии любой алфавитно-цифровой клавиши на клавиатуре вырабатывается сообщение
WM_CHAR
П
Р И МЕ Ч А Н И Е

На самом деле генерируются сообщения о нажатии и отпускании клавиши
WM_KEYDOWN
,
WM_KEYUP
и лишь затем WM_CHAR
Чтобы обработать это сообщение, необходимо добавить в переключатель оконной функции еще одну строку альтернативы: case WM_CHAR: и описать необходимые действия для обработки нажатия клавиши.
Поставим самую простую задачу — при нажатии на клавишу выводить текущий символ в окне приложения, т. е. обеспечить эхо-печать в одну строку, пока не бес- покоясь о выходе строки за границу окна.
Для решения этой задачи воспользуемся шаблонным классом basic_string<>
и создадим класс
String
, который будет работать как с С-строкой, так и со строкой
Unicode. Для этого в качестве типа данных используем
TCHAR

Глава 1

22
П
Р И МЕ Ч А Н И Е

В библиотеке
STL
(Standard Template Library) описаны классы string и wstring, ко- торые являются реализацией базового класса basic_string<>
для типов char и wchar_t соответственно. В
Visual Studio
2010 этот класс размещен в файле xstring и принадлежит стандартному пространству имен std, как и вся библиотека
STL.
Рассмотрим в листинге 1.3 оконную функцию задачи, удалив для простоты обра- ботку сообщений меню.
Листинг

1.3. Программа эхо
-
печати

#include typedef std::basic_string, std::allocator > String;
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lPa- ram)
{
PAINTSTRUCT ps;
HDC hdc; static String str; switch (message)
{ case WM_CHAR:
str += (TCHAR)wParam;

InvalidateRect(hWnd, NULL, TRUE);
break; case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);

TextOut(hdc, 0, 0, str.data(), str.size());

EndPaint(hWnd, &ps);
break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam);
} return 0;
}
Для ускорения компиляции не будем подключать всю область стандартных имен и явно укажем область видимости "
std::
" для всех переменных из этой области.
У нас имеется две возможности описания переменной str
— либо на глобальном уровне, либо в статической области памяти оконной функции. static String str;
Дело в том, что после ввода очередного символа оконная функция теряет управле- ние и, если это будет автоматическая переменная, созданная на стеке, она потеряет свое значение после выхода из функции.


Интерфейс
Windows-
приложения

23
При обработке сообщения
WM_CHAR
извлекаем очередной символ из младшего слова wParam и добавляем к строке перегруженным оператором "
+=
".
Теперь нужно вывести символ в окно. Можно это сделать здесь же, но так посту- пать не принято. Весь вывод обычно стараются осуществлять при обработке сооб- щения
WM_PAINT
. Дело в том, что инициатором перерисовки окна может быть не только само приложение, но и операционная система. Поскольку Windows является системой многозадачной, то окно приложения может быть полностью или частич- но перекрыто окном другого приложения, или окном системной утилиты. В этом случае возникает необходимость восстановления окна либо его части. Операцион- ная система Windows решает эту задачу, объявляя "недействительным прямо-
угольником" либо все окно, либо его часть. Такое объявление автоматически при- водит к генерации сообщения
WM_PAINT
для этого окна.
Если мы будем осуществлять вывод в окно при обработке любого другого сообще- ния, то потеряем его при перерисовке окна, инициированного системой. То же са- мое произойдет при "сворачивании" и "распахивании" окна.
П
Р И МЕ Ч А Н И Е

В
Windows принято, что за содержимое окна несет ответственность приложение, его создавшее. Операционная система может лишь послать сообщение о необходимости перерисовки окна или его части.
Когда необходимо перерисовать окно, его объявляют недействительным. Для этого имеется функция
InvalidateRect()
:
BOOL WINAPI InvalidateRect(HWND hWnd, CONST RECT *lpRect, BOOL bErase)
,
которая объявляет недействительный прямоугольник
*lpRect в окне hWnd
Воспользуемся этим приемом, указывая вторым параметром
NULL
, что приведет к перерисовке всего окна. Значение
TRUE
третьего параметра является указанием перерисовать фон окна.
Теперь рассмотрим вывод строки в окно приложения в сообщении
WM_PAINT
. Для этого необходимо получить контекст устройства. В Windows все функции, выво- дящие что-либо в окно, используют в качестве параметра дескриптор контекста
устройства
hdc
, который представляет собой структуру, описывающую свойства данного устройства вывода. В оконной функции опишем эту переменную:
HDC hdc;
В обработчике сообщения
WM_PAINT
вызовом функции
BeginPaint
() получим hdc
:
HDC WINAPI BeginPaint(HWND hWnd, LPPAINTSTRUCT lpPaint);
Вся необходимая информация для перерисовки окна будет представлена в структу- ре
PAINTSTRUCT
: struct PAINTSTRUCT {
HDC hdc; //Контекст устройства
BOOL fErase;
//Если TRUE — фон окна перерисовывается
RECT rcPaint;
//Недействительный прямоугольник
BOOL fRestore;
//Резерв

Глава 1

24
BOOL fIncUpdate; //Резерв
BYTE rgbReserved[32]; //Резерв
};
Теперь можно выводить текст в окно с помощью функции
TextOut()
:
BOOL WINAPI TextOutW(HDC hdc, int x, int y, LPCWSTR str, int len); которая принимает в качестве параметров контекст устройства hdc
, (x,y) — коор- динаты начала вывода текста, указатель на символьный массив str и длину выво- димой строки len
. Среди GDI-функций нет функции, выводящей отдельный сим- вол, поэтому мы будем выводить всю строку полностью с начальной позиции, чтобы не вычислять каждый раз позицию "нового" символа. Функция
TextOut() не требует строкового типа, ей достаточно указателя первого символа массива, по- скольку следующим параметром задается количество выводимых символов, поэто- му мы можем воспользоваться методами класса
String и получить требуемый ука- затель массива символов и размер строки:
TextOut(hdc, 0, 0, str.data(), str.size());
Вывод осуществляется с начала окна (
0,0
). По умолчанию система координат име- ет начало в левом верхнем углу клиентской области окна (т. е. внутри рамки, ниже заголовка и строки меню), ось x направлена по горизонтали вправо, ось y — вниз
(рис. 1.10). Одна логическая единица равна 1 пикселу.

Рис.
1.10.
Вид окна программы эхо
- печати
Завершает обработчик сообщения функция
EndPaint()
:
BOOL WINAPI EndPaint(HWND hWnd, CONST PAINTSTRUCT *lpPaint)
,
которая обеспечивает освобождение контекста устройства. Необходимо учитывать, что контекст устройства является критически важным ресурсом операционной сис- темы, и, после того как необходимость в данном контексте отпадет, его нужно уничтожить, т. е. освободить этот ресурс.
П
Р И МЕ Ч А Н И Е

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


Интерфейс
Windows-
приложения

25
Еще одно обстоятельство нужно учитывать при выводе в окно — только пара функций
BeginPaint() и
EndPaint()
удаляют из очереди сообщение
WM_PAINT
по- сле его обработки. Если же сообщение не удалить, система будет "до бесконечно- сти" перерисовывать окно.
Сообщение
WM_PAINT
является асинхронным и имеет достаточно низкий приоритет.
Это приводит к тому, что может сложиться ситуация, когда окно получает сообще- ние
WM_PAINT
, а предыдущее сообщение еще не обработано. В этом случае опера- ционная система "складывает" недействительные прямоугольники и объединяет перерисовку окна в одном сообщении.
Сообщение мыши

Нажатие на кнопки мыши приводит к ряду сообщений, вот некоторые из них:

WM_LBUTTONDOWN
— нажатие на левую кнопку мыши;

WM_LBUTTONUP
— отпускание левой кнопки мыши;

WM_RBUTTONDOWN
— нажатие на правую кнопку мыши;

WM_RBUTTONUP
— отпускание правой кнопки мыши;

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

1.4. Обработка сообщений нажатия на кнопку

мыши

TCHAR *r_str = _T("Нажата правая кнопка мыши");
TCHAR *l_str = _T("Нажата левая кнопка мыши");
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lPa- ram)
{
HDC hdc; int x, y; switch (message)
{ case WM_RBUTTONDOWN:
x = LOWORD(lParam);
y = HIWORD(lParam);
hdc = GetDC(hWnd);

TextOut(hdc, x, y, r_str, _tcsclen(r_str));

ReleaseDC(hWnd, hdc);
break; case WM_LBUTTONDOWN:

Глава 1

26
x = LOWORD(lParam);
y = HIWORD(lParam);
hdc = GetDC(hWnd);

TextOut(hdc, x, y, l_str, _tcsclen(l_str));

ReleaseDC(hWnd, hdc);
break; case WM_RBUTTONUP: case WM_LBUTTONUP:

InvalidateRect(hWnd, NULL, TRUE);
break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam);
} return 0;
}
Текстовые строки для вывода в окне мы описали на глобальном уровне, что в дан- ном случае не обязательно. При обработке сообщения о нажатии кнопки мыши можно извлечь текущие координаты курсора из младшего и старшего слова lParam
: x = LOWORD(lParam); y = HIWORD(lParam);
Контекст устройства получаем из функции
GetDC()
, передавая ей параметром де- скриптор окна hWnd
. Выводим текст по координатам курсора. Единственная про- блема здесь — это вычисление длины строки. Если бы это была С-строка, мы обра- тились бы к функции strlen()
, если это строка Unicode — wcslen()
. А в общем случае используем макрос
_tcsclen
, который обеспечит правильный выбор. Соот- ветствующий макрос размещен в файле tchar.h
Освобождаем контекст устройства функцией
ReleaseDC()
При отпускании кнопки мыши хотелось бы очистить окно — это можно сделать, объявив его недействительным функцией
InvalidateRect()
. Причем мы вообще убрали обработчик сообщения
WM_PAINT
, нам достаточно того, что сделает функция обработки сообщения по умолчанию
DefWindowProc()
Иногда бывает полезно отследить состояние регистровых клавиш и при нажатии на кнопку мыши, коды этих клавиш передаются в переменную wParam и могут быть извлечены при обработке сообщения мыши, например: case WM_LBUTTONDOWN: if (MK_SHIFT & wParam)
if (MK_CONTROL & wParam)
{
//Нажаты клавиши Shift и Ctrl
}


Интерфейс
Windows-
приложения

27
else
{

//Нажата клавиша Shift
} else
if (MK_CONTROL & wParam)
{
//Нажата клавиша Ctrl
}
else
{

//Регистровые клавиши не нажаты
} break;
Можно упростить условный оператор, используя возможность логического сложе- ния условий. Так, для проверки одновременного нажатия клавиш и при щелчке левой кнопки мыши запишем выражение: if (MK_SHIFT|MK_CONTROL|MK_LBUTTON == wParam). . .
Создание окна

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

В программе можно установить один или несколько таймеров, которые позволяют производить отсчет времени. Таймер создается функцией
SetTimer()
:
UINT_PTR WINAPI SetTimer(HWND hWnd, UINT_PTR nIDEvent, UINT uElapse,
TIMERPROC lpTimerFunc);
Параметры функции:

hWnd
— дескриптор окна;

nIDEvent
— номер таймера, задается целым числом;

uElapse
— временной интервал таймера в миллисекундах;

lpTimerFunc
— указатель на функцию, вызываемую при обработке таймера.
Функция таймера определена следующим образом:
VOID CALLBACK TimerProc(
HWND hWnd,
// Дескриптор окна
UINT uMsg,
// WM_TIMER сообщение
UINT_PTR idEvent,
// Номер таймера

Глава 1

28
DWORD dwTime // Текущее системное время
); если lpTimerFunc = NULL
, для обработки сообщения таймера вызывается функция окна-владельца. Обычно так и поступают.
Рассмотрим в листинге 1.5 задачу отсчета времени с момента начала работы при- ложения. Создадим таймер при создании окна в обработчике сообщения
WM_CREATE
:
SetTimer(hWnd, 1, 1000, NULL);
Таймеру присвоим номер 1, а интервал отсчета зададим в 1000 миллисекунд
(1 секунда), в качестве же обработчика таймера используем оконную функцию.
В обработчике сообщения от таймера
WM_TIMER увеличим переменную t
на 1 секунду и объявляем окно недействительным: t++;
InvalidateRect(hWnd, NULL, TRUE);
Cтроку текста для вывода в окне сформируем в обработчике сообщения
WM_PAINT
:
_tcscat(str, _itot(t, s, 10));
TextOut(hdc, 0, 0, str, _tcsclen(str));
К строке str добавим значение переменной t
, для чего преобразуем ее в символь- ный вид. Здесь мы опять воспользовались макросами из tchar.h
:

С-строка —
_tcscat преобразуется в strcat
,
_itot в
_itoa
;

Unicode —
_tcscat преобразуется в wcscat
,
_itot в
_itow
В функциях _
itoa()
,
_itow() для преобразования целого числа в строку, третий параметр — основание системы счисления 10, возвращаемое значение — указатель на строку результата s
. Функцией strcat()
или wcscat()
"приклеим" к исходной строке с текстом "Секунды: " и выведем полученную строку функцией
TextOut()
Перед закрытием окна уничтожим таймер функцией
KillTimer()
:
BOOL WINAPI KillTimer(HWND hWnd, UINT_PTR uIDEvent);
Второй параметр функции uIDEvent
— номер таймера — имеет тип
UINT_PTR
, эк- вивалентный unsigned int для 32-разрядных приложений и введен для совмести- мости с 64-разрядными приложениями, где он уже ассоциируется с unsigned
__int64
Листинг

1.5. Оконная функция для таймера

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc; static int t;
TCHAR s[10], str[20] = _T("Секунды: "); switch (message)


Интерфейс
Windows-
приложения

29
{ case WM_CREATE :

SetTimer(hWnd, 1, 1000, NULL);
break; case WM_TIMER :
t++;

InvalidateRect(hWnd, NULL, TRUE);
break; case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);

_tcscat(str+9, _itot(t, s, 10));

TextOut(hdc, 0, 0, str, _tcsclen(str));

EndPaint(hWnd, &ps);
break; case WM_DESTROY:

KillTimer(hWnd, 1);

PostQuitMessage(0);
break; default: return DefWindowProc(hWnd, message, wParam, lParam);
} return 0;
}
Рисование в окне

При рисовании в окне можно использовать достаточно большой набор GDI- функций. Действуют следующие соглашения: все линии рисуются текущим пером, а области заполняются текущей кистью. Как устанавливать перо и кисть мы обсу- дим позднее, а пока будем рисовать пером и кистью по умолчанию. Все эти функ- ции возвращают ненулевое значение в случае успешного выполнения, и 0 — в слу- чае ошибки. Графические функции работают с логическими координатами, одна логическая единица по умолчанию равна одному пикселу.
П
Р И МЕ Ч А Н И Е

Пиксел

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

Для того чтобы нарисовать линию, используется функция
LineTo()
:
BOOL WINAPI LineTo(HDC hdc, int x, int y); где hdc
— дескриптор контекста устройства, x
, y
— координаты конца линии. На- чало линии определяется положением текущей позиции рисования.

Глава 1

30
При создании окна текущая позиция определяется в начале координат
(0,0)
. Если же необходимо нарисовать линию из другой точки, положение текущей позиции рисования может быть изменено.
Установка текущей позиции

Функция
MoveToEx()
:
BOOL WINAPI MoveToEx(HDC hdc, int x, int y, LPPOINT oldPoint); устанавливает текущую позицию в точку с координатами
(x,y)
и передает в струк- туру
POINT
координаты предыдущей позиции. typedef struct tagPOINT
{ LONG x;
LONG y;
} POINT;
Если последний параметр
NULL
, то предыдущие координаты не сохраняются.
Обычно эти функции используются в паре, обе они возвращают ненулевое значе- ние, в случае успешного завершения, и
0
— в случае ошибки.
Например, если нам нужно нарисовать линию между двумя точками, можно сде- лать это так:
MoveToEx(hdc, 50, 100, NULL);
LineTo(hdc, 350, 100);
Определение размера клиентской области

При выводе данных в окно необходимо определить фактический размер этого окна, точнее, его клиентской области. Для решения данной задачи существует функция
GetClientRect()
:
BOOL WINAPI GetClientRect(HWND hWnd, LPRECT lpRect);
Размеры клиентской области окна возвращаются в структуре
RECT
: typedef struct tagRECT
{ LONG left;
//x – координата левого верхнего угла
LONG top;
//y - координата левого верхнего угла
LONG right;
//x – координата правого нижнего угла
LONG bottom; //y - координата правого нижнего угла
} RECT;

Каталог: uploads
uploads -> Научно-исследовательская работа Путешествие в мир компьютера Севастьянов Вадим
uploads -> «деревянная игрушка коми пермяцкого народа»
uploads -> Викторина «Я хочу здоровым быть»
uploads -> «чем великобритания интересна для россии?» Великобритания входит в число крупнейших мировых держав
uploads -> Персональные компьютеры, история создания и развития
uploads -> Подросток и компьютерные игры
uploads -> Руководство пользователя 2 Заключение 12
uploads -> Сборник Из опыта проектной деятельности учащихся гимназии №524 в 2012-2013 учебном году Санкт-Петербург 2013


Поделитесь с Вашими друзьями:
1   2   3   4   5   6   7   8   9   ...   15


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

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


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