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



Pdf просмотр
страница3/7
Дата28.11.2016
Размер0.9 Mb.
Просмотров1532
Скачиваний0
1   2   3   4   5   6   7
Бит
Значение
15
Установлен, если клавиша отпущена; в противном случае сброшен
14
Установлен, если клавиша была нажата до того, как послано сообщение; сброшен, если клавиша не была нажата
13
Установлен, если также нажата клавиша ALT, иначе сброшен
12 – 11
Используется Windows
10 – 9
В настоящий момент не используется
8
Установлен, если нажатая клавиша является функциональной или находится на расширенной клавиатуре; сброшен в противном случае
7 – 0
Код клавиши, зависящий от производителя (т. е. скэн-код)
Пока для нас важным является лишь параметр Ch, так как он опре- деляет код нажатой клавиши.
Для обработки сообщения WM_CHAR необходимо поместить его макрокоманду в очередь сообщений, добавить прототип соответствую-

22
щего обработчика в объявление класса, предназначенного для создания главного окна, и включить в программу реализацию функции OnChar.
Вот, например, версия OnChar, в которой поступающие символы отоб- ражаются на экране: char str[80]; // строка для вывода
//
Обработка сообщения
WM_CHAR afx_msg void CMainWin::OnChar (UINT ch, UINT count, UINT flags)
{
CClientDC dc(this); dc.TextOut (1, 1, "
", 3); // стирает предыдущий символ wsprintf(str, "%c", ch); dc.TextOut(1, 1, str, strlen (str));
}
Смысл кода внутри OnChar весьма прост: введенный символ дубли- руется на экране (так называемый режим «эха»). Должно быть, вы удив- лены, что для решения столь тривиальной задачи потребовалось так мно- го строк. Причина заключается в том, что Windows должна организовать связь между программой и экраном. Это называется контекстом
устройства, или просто DC (Device Context), и достигается путем объяв- ления объекта типа CClientDC, порождаемого от CDC. Класс CClientDC позволяет предоставить контекст устройства для рабочей области окна.
Более точное определение контекста устройства мы рассмотрим далее.
Для начала достаточно знать, что, получив контекст устройства, про- грамма может начать выводить текст на экран.
Функция, предназначенная для отображения символа, называется
TextOut и является членом класса CDC. Есть два варианта этой функции.
Их прототипы таковы: virtual BOOL CDC::TextOut(int X, int Y, LPCSTR lpszStr, int Length);
BOOL CDC::TextOut(int X, int Y, const CString &StrOb);
В первом случае текстовая строка, адресуемая указателем lpszStr, выводится в окно в точке с координатами, которые содержатся в пара- метрах Х и Y (по умолчанию координаты приводятся в пикселях). Длина строки задается параметром Length. Во втором случае выводится строка, содержащаяся в переменной StrOb. CString – это один из специальных выделенных классов в MFC. Он предназначен для работы со строками.
Мы будем работать с первым вариантом TextOut. В обоих случаях функ- ция TextOut возвращает либо ненулевое значение при успешном завер- шении, либо ноль.
Каждый раз при вызове OnChar символ, введенный пользователем, записывается с помощью функции wsprintf в строку единичной длины, а затем с помощью TextOut отображается в окне в позиции с координатами

23
(1, 1). Переменная str объявлена глобальной, потому что должна сохра- нять свое значение между вызовами функции в последующих примерах.
Верхний левый угол рабочей области окна имеет координаты (0, 0).
Координаты точки окна всегда относятся к самому окну, а не к экрану, поэтому вводимый символ будет отображаться в верхнем левом углу независимо от действительного расположения окна на экране.
Цель первого вызова функции TextOut – стереть любой ранее отоб- раженный символ. Поскольку Windows – это графическая система, сим- волы в ней могут иметь разный размер, и замена одного символа другим не обязательно приведет к полному удалению первого. Например, если символ «w» заменяется символом «i», то часть «w» все равно останется на экране, если только не будет удалена специально.
Сначала вам может показаться, что использование TextOut для выво- да одного символа не совсем рационально. Дело в том, что в MFC (и во- обще в Windows) нет функции, предназначенной просто для вывода сим- вола. Большую часть диалога с пользователем Windows осуществляет че- рез диалоговые окна, меню, панели инструментов и т. д. Поэтому функ- ций для вывода текста в рабочую область окна совсем немного.
Ниже приведен пример программы обработки строк, начинающийся с библиотечного файла MESSAGE1.H, содержащего объявления порож- даемых классов.
Задание № 1. Исследуйте программу (листинг № 2). Изучите каждую строку. Создайте обработчик сообщения от клавиатуры, который бу- дет выполнять другие действия, чем в листинге № 2.
Листинг № 2. Обработка сообщения WM_CHAR
//
Файл
MESSAGE1.H –
Обработка сообщения
//
Это класс
, предназначенный для создания главного окна class CMainWin:public CFrameWnd
{ public:
CMainWin(); afx_msg void OnChar(UINT ch, UINT count, UINT flags);
DECLARE_MESSAGE_MAP()
};
//
Это класс
, предназначенный для создания приложения class CApp:public CWinApp
{ public:
BOOL InitInstance(); };
//
Файл
MESSAGE1.CPP -
Программа
, обрабатывающая сообщение
WM_CHAR
#include
#include
#include "message1.h" char str[80];
// строка для вывода

24
//
Создание окна
CMainWin::CMainWin()
{ Create(NULL, "
Обработка сообщения
WM_CHAR"); };
//
Инициализация приложения
BOOL CApp::InitInstance()
{ m_pMainWnd = new CMainWin; m_pMainWnd -> ShowWindow(m_nCmdShow); m_pMainWnd -> UpdateWindow(); return TRUE;
};
//
Это очередь сообщений приложения
BEGIN_MESSAGE_MAP(CMainWin, CFrameWnd)
ON_WM_CHAR()
END_MESSAGE_MAP()
//
Обработка сообщения
WM_CHAR afx_msg void CMainWin::OnChar(UINT ch, UINT count, UINT flags)
{
CClientDC dc(this); dc.TextOut(1, 1, " ", 3); // стирает предыдущий символ wsprintf(str, "%c", ch); dc.TextOut(1, 1, str, strlen(str));
}
CApp App; // создание экземпляра приложения
Конец листинга № 2
3.6. Контекст устройства
Программа в листинге № 2 для вывода символов на экран должна получать контекст устройства.
В широком смысле, контекст устройства представляет собой путь, по которому приложение Windows, используя соответствую- щий драйвер устройства, выводит информацию в рабочую область окна. Контекст устройства является структурой, полностью опреде- ляющей состояние этого драйвера и способ вывода информации.
Перед тем как приложение начнет выводить информацию в рабочую область окна, оно должно получить контекст устройства. Пока это не сделано, нет связи между программой и окном, предназначенным для вы- вода. А поскольку контекст устройства требуется для работы TextOut и других функций вывода, то это правило становится обязательным.
Поскольку Windows может предоставить лишь небольшое число контекстов, важно, чтобы программа освободила контекст по окончании работы с ним. К счастью, в MFC есть классы, способные управлять этим процессом. Например, при создании экземпляра объекта типа CClientDC программе выделяется контекст устройства. Если этот объект необходи- мо удалить, то вызывается функция ReleaseDC и контекст устройства ав-

25
томатически освобождается. В этом одно из преимуществ использования библиотеки MFC.
При создании объекта типа CClientDC программе предоставляется контекст устройства, относящийся к рабочей области окна. Конструктор класса CClientDC таков:
CClientDC(CWnd *Window);
Параметр Window является указателем на окно, которому выделяется контекст устройства. Для активного окна можно указать ключевое слово this. Если контекст устройства не может быть предоставлен, то конструк- тор CClientDC обращается к классу CResourceException.
Таким образом, всякий раз, когда необходимо вывести информацию в рабочую область окна, необходимо получить контекст устройства с по- мощью CClientDC. Однако существует особый случай, когда контекст устройства должен быть получен по-другому. Это происходит при обра- ботке сообщения WM_PAINT, рассматриваемого далее.
3.7. Обработка сообщения WM_PAINT
Задание № 2. Запустите программу из листинга № 2 и введите символ, например «а». А затем минимизируйте окно и верните его в прежнее состояние.
В результате выполнения задания № 2 вы обнаружите, что послед- ний введенный символ не будет отображен. Аналогично, если окно за- крыть другим окном, а затем вновь активизировать, символ не будет за- ново отображен. Причина этого проста: в общем случае Windows не со- храняет содержимое окна. Чтобы этого избежать, каждый раз, когда со- держимое окна должно быть обновлено, программе посылается сообще- ние WM_PAINT. И каждый раз, когда программа получает это сообще- ние, она должна обновить содержимое окна.
Обработчик сообщения WM_PAINT называется OnPaint. Его прото- тип таков: afx_msg void OnPaint();
Макрокоманда для этого сообщения называется ON_WM_PAINT.
Для обработки сообщения WM_PAINT необходимо добавить прототип его обработчика в объявление класса CMainWin, включить соответству- ющую макрокоманду в очередь сообщений и определить тело обработчи- ка. Например, приводимый ниже обработчик позволяет вывести на экран текущее содержимое строки str, т. е. последний введенный символ.
//
Обработка сообщения
WM_PAINT afx_msg void CMainWin::OnPaint ()
{

26
CPaintDC dc (this);
// следующая строка предназначена для отображения
// последнего введенного символа dc.TextOut(1, 1, str, strlen (str));
}
Прежде всего обратите внимание, что контекст устройства получает- ся путем создания объекта типа CPaintDC, а не CClientDC. По различным причинам при обработке сообщения WM_PAINT контекст устройства должен быть получен именно таким образом. Конструктор класса
CPaintDC имеет следующий прототип:
CPaintDC(CWnd *Window);
В общем он идентичен конструктору CClientDC. Но класс CPaintDC включает одну важную переменную, называемую m_ps, которая содер- жит структуру типа PAINTSTRUCT. Когда создается объект типа
CPaintDC, то в этой структуре содержится информация, относящаяся к текущему состоянию контекста устройства. Структура PAINTSTRUCT определена следующим образом: typedef struct tagPAINTSTRUCT {
HDC hdc;
// дескриптор контекста устройства
BOOL
fErase;
// TRUE, если фон окна нужно стереть
RECT rcPaint;
// координаты области окна
, которую необ
- ходимо перерисовать
BOOL fRestore;
// зарезервировано
BOOL fIncUpdate;
// зарезервировано
BYTE rgbReserved[16];// зарезервировано
}PAINTSTRUCT;
Здесь hdc – это дескриптор для контекста устройства. В традицион- ных Windows-программах с контекстами устройств работают именно че- рез дескрипторы. Однако в MFC используются объекты, поэтому потреб- ность в дескрипторах возникает редко. Если фон окна должен быть стерт, то переменная fErase будет содержать ненулевое значение. В переменной
rcPaint содержатся координаты области окна, которая должна быть пере- рисована. Тип RECT был описан в первой лабораторной работе. Исполь- зовать содержимое rcPaint нам не придется, поскольку мы предполагаем, что перерисовать нужно все окно. Значения fRestore, fIncUpdate, rgbRe-
served зарезервированы и используются внутри Windows.
Ниже приведен пример программы (листинг № 3), обрабатывающей сообщение WM_PAINT. Первым показан файл MESSAGE2.H, содержа- щий объявления порождаемых классов.
Следует подчеркнуть, что в большинстве реальных проектов обра- ботка WM_PAINT значительно сложнее, так как обычно окна содержат гораздо больше информации, которую необходимо обновлять.

27
Реализация восстановления содержимого окна возможна тремя спо- собами. Во-первых, можно просто вычислять координаты выводимых символов. Это легче всего осуществлять, если не предполагается ввод данных пользователем. Во-вторых, иногда можно хранить запись после- довательности событий, произошедших в окне, и воспроизводить их, ко- гда необходимо обновить содержимое окна. И, наконец, в программе мо- жет поддерживаться так называемое виртуальное окно, содержимое ко- торого просто копируется в реальное окно каждый раз при его обновле- нии. Это наиболее общий метод. Какой из способов лучше, зависит от приложения.
Задание № 3. Наберите, откомпилируйте и запустите программу
(листинг № 3 ). Найдите отличия в работе программы по сравнению с программой в листинге № 2.
Листинг № 3. Обработка сообщений WM_CHAR и WM_PAINT
//
Файл
MESSAGE2.H -
Обработка сообщений
WM_CHAR и
WM_PAINT class CMainWin:public CFrameWnd
{ public:
CMainWin(); afx_msg void OnChar(UINT ch, UINT count, UINT flags); afx_msg void OnPaint();
DECLARE_MESSAGE_MAP() }; class CApp:public CWinApp
{ public:
BOOL InitInstance(); };
//
Файл
MESSAGE2.CPP
//
Программа
, обрабатывающая сообщения
WM_CHAR и
WM_PAINT
#include
#include
#include "message2.h" char str[80] = "
Пример "; // строка для вывода
//
Создание окна
CMainWin::CMainWin()
{ Create(NULL, "
Обработка сообщения
WM_PAINT"); };
BOOL CApp::InitInstance()
{ m_pMainWnd = new CMainWin; m_pMainWnd -> ShowWindow(m_nCmdShow); m_pMainWnd -> UpdateWindow(); return TRUE; };
BEGIN_MESSAGE_MAP(CMainWin, CFrameWnd)
ON_WM_CHAR()
ON_WM_PAINT()
END_MESSAGE_MAP()

28
//
Обработка сообщения
WM_CHAR afx_msg void CMainWin::OnChar(UINT ch, UINT count, UINT flags)
{
CClientDC dc(this); dc.TextOut(1, 1, " ", 3); // стирает предыдущий символ wsprintf(str, "%c", ch); dc.TextOut(1, 1, str, strlen(str));
}
//
Обработка сообщения
WM_PAINT afx_msg void CMainWin::OnPaint()
{
CPaintDC dc(this);
// отображение последнего введенного символа dc.TextOut(1, 1, str, strlen(str));
}
CApp App; // создание экземпляра приложения
Конец листинга № 3
3.8. Обработка сообщений мыши
Поскольку Windows является операционной системой, в значитель- ной степени ориентированной на работу с мышью, все программы для
Windows должны определенным образом реагировать на действия мыши.
Учитывая важность этого момента, в Windows имеется несколько типов сообщений мыши. В данном пункте рассматриваются два наиболее об- щих из них, WM_LBUTTONDOWN и WM_RBUTTONDOWN, генериру- емые при нажатии левой и правой кнопок мыши, соответственно. Обра- ботчиками этих сообщений являются функции OnLButtonDown и
OnRButtonDown. Их прототипы таковы: afx_msg void OnLButtonDown(UINT Flags, CPoint Loc); afx_msg void OnRButtonDown(UINT Flags, CPoint Loc);
Значение параметра Flags указывает на то, была ли при генерировании сообщения нажата клавиша [CTRL], [SHIFT] или другая кнопка мыши. Па- раметр может содержать любую комбинацию из следующих значений:
MK_CONTROL
MK_SHIFT
MK_MBUTTON
MK_RBUTTON
MK_LBUTTON
Если одновременно с кнопкой мыши была нажата клавиша [CTRL], то параметр Flags будет содержать MK_CONTROL. Если была нажата клавиша [SHIFT], то значением Flags будет MK_SHIFT. Если при нажа- тии левой кнопки мыши удерживалась и правая кнопка, значением будет
MK_RBUTTON. И, наоборот, если при нажатии правой кнопки удержи- валась левая, значением будет MK_LBUTTON. Если же при нажатии од- ной из этих кнопок удерживалась также средняя кнопка (при ее наличии и поддержки драйверами мыши), то параметр Flags будет содержать
MK_MBUTTON. Одновременно может быть установлено несколько та- ких значений.

29
Параметр Loc, являющийся объектом типа CPoint, содержит коорди- наты курсора мыши в момент нажатия кнопки. Класс CPoint порождается от структуры POINT, определенной следующим образом: typedef struct tagPOINT{
LONG x;
LONG у
;
} POINT;
Структура POINT содержит координаты (X,Y) курсора мыши. Таким образом, координата Х находится в переменной Loc.x, а координата Y – в переменной Loc.y.
В следующем примере (листинг № 4) в нашу программу вносятся необходимые элементы, позволяющие ей обрабатывать сообщения мы- ши. Каждый раз при нажатии левой или правой кнопки мыши выводится строка сообщения, которая отображается в позиции курсора мыши.
Листинг № 4. Обработка сообщений мыши
//
Файл
MESSAGE3.H -
Обработка сообщений мыши class CMainWin:public CFrameWnd
{ public:
CMainWin(); afx_msg void OnChar(UINT ch, UINT count, UINT flags); afx_msg void OnPaint(); afx_msg void OnLButtonDown(UINT flags, CPoint loc); afx_msg void OnRButtonDown(UINT flags, CPoint loc);
DECLARE_MESSAGE_MAP() }; class CApp:public CWinApp
{ public:
BOOL InitInstance(); };
//
Файл
MESSAGE3.CPP -
Программа
, обрабатывающая сообщения от мыши
#include
#include
#include "message3.h" char str[80] = "
Пример "; // строка для вывода
CMainWin::CMainWin()
{ Create(NULL, "
Обработка сообщений от мыши "); };
BOOL CApp::InitInstance()
{ m_pMainWnd = new CMainWin; m_pMainWnd -> ShowWindow(m_nCmdShow); m_pMainWnd -> UpdateWindow(); return TRUE; };
BEGIN_MESSAGE_MAP(CMainWin, CFrameWnd)
ON_WM_CHAR()
ON_WM_PAINT()
ON_WM_LBUTTONDOWN()
ON_WM_RBUTTONDOWN()

30
END_MESSAGE_MAP() afx_msg void CMainWin::OnChar(UINT ch, UINT count, UINT flags)
{
CClientDC dc(this); dc.TextOut(1, 1, " ", 3); // стирает предыдущий символ wsprintf(str, "%c", ch); dc.TextOut(1, 1, str, strlen(str));
} afx_msg void CMainWin::OnPaint()
{
CPaintDC dc(this); dc.TextOut(1, 1, str, strlen(str)); }
//
Обработка нажатия левой кнопки мыши afx_msg void CMainWin::OnLButtonDown(UINT flags,CPoint loc)
{
CClientDC dc(this); wsprintf(str, "
Нажата левая кнопка мыши "); dc.TextOut(loc.x, loc.y, str, strlen(str)); }
//
Обработка нажатия правой кнопки мыши afx_msg void CMainWin::OnRButtonDown(UINT flags,CPoint loc)
{
CClientDC dc(this); wsprintf(str, "
Нажата правая кнопка мыши "); dc.TextOut(loc.x, loc.y, str, strlen(str)); }
CApp App; // создание экземпляра приложения
Конец листинга № 4
Рис. 2. Пример работы программы обработки сообщений мыши (листинг № 4)
Задание № 4. Запустите программу (листинг № 4), изучите ее работу. Напишите собственный обработчик сообщений мыши, кото- рый использует информацию сообщения от мыши.
3.9. Генерация сообщения WM_PAINT
Программа сама может вызвать генерацию сообщения WM_PAINT.
Это необходимо, так как процесс перерисовки окна может занимать до-

31
статочно много времени, ведь Windows – это многозадачная система и в ней одновременно могут выполняться несколько программ, каждой из которых также требуются ресурсы процессора. Поэтому программе нуж- но просто сообщить Windows о необходимости вывода информации, но предоставить системе самой решить, когда это лучше всего выполнить.
Такой подход позволяет Windows эффективнее распределять ресурсы процессора между всеми выполняющимися задачами. В этом случае про- грамма приостанавливает вывод на экран до тех пор, пока не будет полу- чено сообщение WM_PAINT.
В предыдущих примерах сообщение WM_PAINT принималось толь- ко тогда когда окно не было перекрыто. Но если вывод на экран задержи- вается до принятия WM_PAINT, то для организации интерактивного вво- да-вывода необходимо найти способ сообщить Windows о том, что она должна послать сообщение WM_PAINT в окно, если вывод не завершен.
Чтобы побудить Windows послать сообщение WM_PAINT, програм- ма должна вызвать функцию InvalidateRect, являющуюся членом класса
CWnd. Прототип функции таков: void CWnd::InvalidateRect(LPCRECT IpRegion, BOOL Erase = TRUE);
Здесь параметр IpRegion является указателем на объект типа CRect, определяющий область внутри окна, которая должна быть перерисована.
Класс CRect порождается от структуры типа RECT, описанной выше. Ко- ординаты обновляемой области определятся полями структуры RECT.
Однако если значение параметра равно NULL, то обновить необходимо содержимое всего окна. Если параметр Erase не равен нулю, то нужно стереть фон окна. Если значение параметра равно нулю, то фон будет оставлен без изменений.
При вызове функции InvalidateRect системе сообщается о том, что содержимое окна некорректно и должно быть обновлено. Это, в свою очередь, приводит к тому, что Windows посылает программе сообщение
WM_PAINT.
Ниже приводится переработанный вариант предыдущей программы
(листинг № 5), в котором весь процесс вывода направляется с помощью обработчика сообщения WM_PAINT. Каждый раз, когда необходимо осуществить вывод, посылается сообщение WM_PAINT. Другие обра- ботчики просто подготавливают предназначенную для вывода информа- цию, а затем вызывают функцию InvalidateRect.
Листинг № 5. Генерация сообщения WM_PAINT
//
Файл
MESSAGE4.H - генерация сообщения
WM_PAINT class CMainWin:public CFrameWnd
{ public:

32
CMainWin(); afx_msg void OnChar(UINT ch, UINT count, UINT flags); afx_msg void OnPaint(); afx_msg void OnLButtonDown(UINT flags, CPoint loc); afx_msg void OnRButtonDown(UINT flags, CPoint loc);
DECLARE_MESSAGE_MAP() }; class CApp:public CWinApp
{ public: BOOL InitInstance(); };
//
Файл
MESSAGE4.CPP - программа
, генерирующая сообщение
WM_PAINT
#include
#include
#include "message4.h" char str[80] = "
Пример "; // строка для вывода int X = 1, Y = 1;
// текущая позиция вывода
CMainWin::CMainWin()
{ Create(NULL, "
Генерация сообщения
WM_PAINT"); };
BOOL CApp::InitInstance()
{ m_pMainWnd = new CMainWin; m_pMainWnd -> ShowWindow(m_nCmdShow); m_pMainWnd -> UpdateWindow(); return TRUE; };
BEGIN_MESSAGE_MAP(CMainWin, CFrameWnd)
ON_WM_CHAR()
ON_WM_PAINT()
ON_WM_LBUTTONDOWN()
ON_WM_RBUTTONDOWN()
END_MESSAGE_MAP()
//
Обработка сообщения
WM_CHAR afx_msg void CMainWin::OnChar(UINT ch, UINT count, UINT flags)
{
X = Y = 1; wsprintf(str, "%c", ch);
InvalidateRect(NULL); }
//
Обработка сообщения
WM_PAINT afx_msg void CMainWin::OnPaint()
{
CPaintDC dc(this); dc.TextOut(X, Y, str, strlen(str)); }
//
Обработка нажатия левой кнопки мыши afx_msg void CMainWin::OnLButtonDown(UINT flags,CPoint loc)
{ wsprintf(str, "
Нажата левая кнопка мыши ");
X = loc.x;
Y = loc.y;
InvalidateRect(NULL); }
//
Обработка нажатия правой кнопки мыши afx_msg void CMainWin::OnRButtonDown(UINT flags,CPoint loc)
{ wsprintf(str, "
Нажата правая кнопка мыши ");

33
X = loc.x;
Y = loc.y;
InvalidateRect(NULL); }
CApp App; // создание экземпляра приложения
Конец листинга № 5
Задание № 5. Изучите программу (листинг № 5). Испытайте ее для различных ситуаций. Последовательно удалите в обработчиках сообщений вызов функции InvalidateRect и изучите поведение про- граммы.
Заметьте, что в программе добавились две новые глобальные пере- менные, Х и Y, содержащие координаты позиции окна, в которой будет выведен текст при получении сообщения WM_PAINT.
Как видите, организация вывода на основе сообщения WM_PAINT позволила немного сократить программу и сделать ее, в определенном смысле, более понятной.



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


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

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


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