Партнерка на США и Канаду по недвижимости, выплаты в крипто

  • 30% recurring commission
  • Выплаты в USDT
  • Вывод каждую неделю
  • Комиссия до 5 лет за каждого referral

Обработчик сообщения должен быть помещен в описание класса, ответственного за создание окна. Это выполняется включением прототипа функции в объявление класса. В следующем примере обработчик
OnChar включается в класс CMainWin:

class CMainWin:public CFrameWnd

{ public:

CMainWin();

afx_msg void OnChar(UINT ch, UINT count, UINT flags);

DECLARE_MESSAGE_MAP()

};

Как видите, для OnChar использован спецификатор типа afx_msg. С помощью него должны быть объявлены все прототипы функций-обработчиков. Теперь, когда OnChar является частью класса CMainWin, этот класс сможет принимать сообщение WM_CHAR.

3.5. Обработка сообщений от клавиатуры

Одно из наиболее распространенных в Windows сообщений генерируется при нажатии клавиши. Оно называется WM_CHAR, и для него используется макрокоманда ON_WM_CHAR. Приложение никогда не получает данные непосредственно от клавиатуры. Вместо этого каждый раз при нажатии клавиши активному окну посылается сообщение WM_CHAR. Чтобы продемонстрировать это, давайте расширим пример из лабораторной работы № 1 (листинг № 1) таким образом, чтобы в нем обрабатывались сообщения от клавиатуры.

Обработчик сообщения WM_CHAR называется OnChar. Его прототип таков:

afx_msg void OnChar(UINT Ch, UINT Count, UINT Flags);

Этот обработчик вызывается всякий раз, когда поступает сообщение WM_CHAR. ASCII-код нажатой клавиши передается в параметре Ch. Количество повторяющихся символов, сгенерированных в результате удерживания клавиши нажатой, передается в параметре Count. Параметр Flags кодируется следующим образом (табл. 4).

НЕ нашли? Не то? Что вы ищете?

Таблица 4

Содержимое параметра Flags

Бит

Значение

15

Установлен, если клавиша отпущена; в противном случае сброшен

14

Установлен, если клавиша была нажата до того, как послано сообщение; сброшен, если клавиша не была нажата

13

Установлен, если также нажата клавиша ALT, иначе сброшен

12 – 11

Используется Windows

10 – 9

В настоящий момент не используется

8

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

7 – 0

Код клавиши, зависящий от производителя (т. е. скэн-код)

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

Для обработки сообщения WM_CHAR необходимо поместить его макрокоманду в очередь сообщений, добавить прототип соответствующего обработчика в объявление класса, предназначенного для создания главного окна, и включить в программу реализацию функции 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 отображается в окне в позиции с координатами (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 <afxwin. h>

#include <string. h>

#include "message1.h"

char str[80]; // строка для вывода

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

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 и контекст устройства автоматически освобождается. В этом одно из преимуществ использования библиотеки 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 ()

{

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, rgbReserved зарезервированы и используются внутри Windows.

Ниже приведен пример программы (листинг № 3), обрабатывающей сообщение WM_PAINT. Первым показан файл MESSAGE2.H, содержащий объявления порождаемых классов.

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

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

Задание № 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 <afxwin. h>

#include <string. h>

#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()

// Обработка сообщения 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. Одновременно может быть установлено несколько таких значений.

Параметр 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 <afxwin. h>

#include <string. h>

#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()

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. Это необходимо, так как процесс перерисовки окна может занимать достаточно много времени, ведь 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:

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 <afxwin. h>

#include <string. h>

#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, "Нажата правая кнопка мыши");

X = loc. x;

Y = loc. y;

InvalidateRect(NULL); }

CApp App; // создание экземпляра приложения

Конец листинга № 5

Задание № 5. Изучите программу (листинг № 5). Испытайте ее для различных ситуаций. Последовательно удалите в обработчиках сообщений вызов функции InvalidateRect и изучите поведение программы.

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

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

3.10. Обработка сообщения WM_DESTROY

Есть одно специальное сообщение, на которое программе также требуется отвечать: WM_DESTROY. Оно посылается окну, когда последнее должно быть закрыто. Если такое сообщение получает главное окно приложения, это означает, что программа завершается. В этом случае приложение должно выполнить все необходимые операции по выгрузке. Обработчик сообщения WM_DESTROY называется OnDestroy, а его прототип таков:

afx_msg void OnDestroy();

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

3.11. Обработка сообщений таймера

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

Для запуска таймера используется функция SetTimer, являющаяся членом класса CWnd. Ее прототип таков:

UINT CWnd::SetTimer(UINT ID, UINT Length,

void(CALLBACK EXPORT *TFunc) (HWND, UINT, UINT, DWORD));

Здесь параметр ID задает числовой идентификатор, который будет ассоциироваться с данным таймером (может быть несколько таймеров). Значение Length определяет длину интервала работы таймера, т. е. промежуток времени, проходящий между двумя прерываниями. Функция, на которую указывает параметр TFunc, является функцией таймера и вызывается в момент прерывания. Это должна быть функция обратного вызова, которая возвращает VOID CALLBACK и принимает параметры тех же типов, что и традиционная функция окна. Если же значение TFunc равно NULL, как это обычно бывает, то сообщения таймера будут посылаться главному окну программы. В таком случае при каждом прерывании таймера сообщение WM_TIMER помещается в очередь сообщений программы и обрабатывается так же, как и все другие сообщения. Этот принцип использован в следующем примере. В случае успеха функция возвращает значение ID. Если таймер недоступен, возвращается ноль.

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

BOOL CWnd::KillTimer(int ID);

Здесь параметр ID является идентификатором соответствующего таймера. Функция KillTimer возвращает ненулевое значение при успешном завершении и ноль – в противном случае.

Сообщения таймера обрабатываются функцией OnTimer, которая имеет такой прототип:

afx_msg void OnTimer(UINT ID);

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

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

Листинг № 6. Программа «Часы», обрабатывает сообщение WM_DESTROY

// Файл MESSAGE5.H - Программа "Часы" - обработка сообщения WM_DESTROY

class CMainWin:public CFrameWnd

{ public:

CMainWin();

afx_msg void OnPaint();

afx_msg void OnTimer(UINT ID);

afx_msg void OnDestroy();

DECLARE_MESSAGE_MAP() };

class CApp:public CWinApp

{ public: BOOL InitInstance(); };

// Файл MESSAGE5.CPP - Программа "Часы"

#include <afxwin. h>

#include <string. h>

#include <time. h>

#include "message5.h"

char str[80] = ""; // строка для вывода

CMainWin::CMainWin()

{ RECT r;

r. left = r. top = 10;

r. right = 200;

r. bottom = 60;

Create(NULL, "Часы", WS_OVERLAPPEDWINDOW, r);

};

// Инициализация приложения

BOOL CApp::InitInstance()

{ m_pMainWnd = new CMainWin;

// Запуск таймера

if (m_pMainWnd -> SetTimer(1, 1000, NULL) != 1) return FALSE;

m_pMainWnd -> ShowWindow(m_nCmdShow);

m_pMainWnd -> UpdateWindow();

return TRUE; };

BEGIN_MESSAGE_MAP(CMainWin, CFrameWnd)

ON_WM_PAINT()

ON_WM_TIMER()

ON_WM_DESTROY()

END_MESSAGE_MAP()

// Обработка сообщения WM_PAINT

afx_msg void CMainWin::OnPaint()

{ CPaintDC dc(this);

// следующая строка предназначена для отображения

// даты и времени

dc. TextOut(1, 1, str, strlen(str)); }

// Обработка сообщения WM_TIMER

afx_msg void CMainWin::OnTimer(UINT ID)

{ CTime curtime = CTime::GetCurrentTime();

struct tm *newtime;

newtime = curtime. GetLocalTm();

wsprintf(str, asctime(newtime));

str[strlen(str)-1] = '\0'; // удаляет символы конца строки

InvalidateRect(NULL, 0);

}

// Завершение приложения – обработка сообщения WM_DESTROY

afx_msg void CMainWin::OnDestroy()

{

KillTimer(1);

}

CApp App; // создание экземпляра приложения

Конец листинга № 6

Рис. 3. Пример работы программы «Часы»

Задание № 6. Изучите программу (листинг № 6). Создайте еще один таймер в программе и напишите код для обработки его сообщения. Не забудьте удалить его при завершении работы приложения.

3.12. Основные типы сообщений Windows

Аппаратные сообщения. Оконная программа получает сообщения, инициируемые тремя частями аппаратного обеспечения: клавиатурой, мышью и системным таймером. Все они генерируют аппаратные прерывания. Поскольку диспетчеризация Windows не действует в режиме прерываний, аппаратные события должны быть буферизованы. Это обеспечивает обработку событий в порядке их поступления. Например, когда вы нажимаете клавишу [К], прерывание уведомляет систему о том, что приготовлены данные от клавиатуры. Драйвер клавиатуры отыскивает эти данные и создает элемент в первичной очереди, откуда система их передает в очередь приложения, а точнее, конкретного потока. Когда наступает его очередь, сообщения, содержащие информацию о клавише [К], направляются в соответствующую оконную процедуру. Сообщения от мыши и системного таймера обрабатываются аналогично. В табл. 5 представлены основные сообщения, которые формируются в ответ на работу аппаратного обеспечения.

Из за большого объема этот материал размещен на нескольких страницах:
1 2 3 4 5 6 7