Исходный текст программы календаря-часов

На рис. 27.1. приведен результат работы приложения 27-1.

//Приложение 27-1. Обработка сообщений Windows

//Файл 27-1.срр

#include <owl\framewin.h>

#include <time.h>

/*Класс приложения, производный от Tapplication*/

class MyApp:public TApplication{

public:

virtual void InitMainWindow(); //Замещаем функцию InitMainWindow

};

//Класс главного окна, производный от TFrameWindow class MyWindow:public TFrameWindow{ private:

char szText[27]; //Поле для даты, пробелов слева и справа и завершающего public:

MyWindow(TWindow*,const char far*); //Конструктор класса MyWindow

void Paint(TDC&,bool,TRect&); //Переопределяем функцию Paint

void EvGetMinMaxInfо(MINMAXINFO far&);// Объявляем функции обработки

void EvTimer(UINT); //сообщений WM_MINMAXINFO, HM_TIMER

int EvCreate(CREATESTRUCT far&);//и WM_CREATE

void OutTime(); //Прикладная функция получения времени

~MyWindow(); //Деструктор класса MyWindow

DECLARE_RESPONSE_TABLE(MyWindow); //Объявляем таблицу откликов

}; DEFINE_RESPONSE_TABLE1(MyWindow,TFrameWindow) //Начинаем таблицу откликов

EV_CREATE //Описываем состав

EV_WM_GETMINMAXINFO, //таблицы

EV_WM_TIMER, //откликов

END_RESPONSE_TABLE; //Завершаем таблицу откликов

/*Конструктор класса MyWindow*/ MyWindow::MyWindow(TWindow*parent,const char far* title):

TframeWindow(parent,title){

Attr.X=0;Attr.Y=0; //Задаем координаты окна

Attr.W=0;Attr.H=0; //Задаем начальные размеры окна

Attr.ExStyle=WS_EX_TOPMOST; //Назначаем главное окно окном переднего плана

} /*Деструктор класса MyWindow*/

MyWindow::~MyWindow(){


Обработка сообщений Windows 245

KillTimer(1); } /*Функции откликов на сообщения*/

int MyWindow::EvCreate(CREATESTRUCT far&){

SetTimerd,1000);//Устанавливаем таймер на частоту 1с

OutTime(); //Сразу же выводим в окно текущее время

return 0;

} void MyWindow::EvGetMinMaxInfо(MINMAXINFO far & mmi){

mmi.ptMinTrackSize.x=185; mmi.ptMinTrackSize.y=45; mmi.ptMaxTrackSize.x=185;

mmi.ptMaxTrackSize.y=45;

} void MyWindow::EvTimer(UINT){

OutTime();

}

/*Функция OutTime() чтения ипреобразования текущего времени*/ void MyWindow::OutTime (){

tm* tmTime;//Указатель на структуру для хранения времени

time_t sec=time(NULL); //Получение числа секунд от 01.01.1970

tmTime=localtime(&sec); //Преобразование в формат даты/времени

strcpy(szText," ");//Один пробел в начале для красоты

strcat(szText,asctime(tmTime)); //Преобразование в коды ASCII

szText[strlen(szText)-1]='\0'; //Замена завершающего CR на нуль

Invalidate(); //Инициирование перерисовки рабочей области окна

}

/*3амещающая функция Paint()*/ void MyWindow::Paint(TDC&dc,bool,TRect&){

dc.TextOut(0,0,szText);

}

/*3амещакщая функция Ini tMainWindow () */ void MyApp::InitMainWindow(){

MyWindow* myWin=new MyWindow(0,"Текущее время");

SetMainWindow(myWin);

}

/*Главная функция приложения OwlMain*/ int OwlMain(int,char*[]){

MyApp* myApp=new MyApp;

return myApp->Run();

}

Таблица откликов и функции обработки сообщений

В предыдущей главе уже отмечалось, что для обработки сообщения WM_PAINT, имеющего особую важность, в OWL предусмотрена упрощенная процедура, заключающаяся в предоставлении программи­сту функции-заглушки Paint(), которую можно заместить прикладной функцией с тем же именем. В за­мещающей функции Paint() оказывается доступен контекст устройства dc, что дает возможность исполь­зовать для вывода в окно приложения весь богатый набор функций GDI.

Для обработки других сообщений Windows (от мыши, пунктов меню, таймера и т.д.) в программе не­обходимо предусмотреть следующие элементы:

• Объявление в классе главного окна таблицы откликов (макрос DECLARE_RESPONSE_TABLE)

• Саму таблицу откликов (макросы DEFINE_RESPONSE_TABLE1 и END_RESPONSE_TABLE, a
также помещаемые между ними макросы, определяющие конкретные сообщения)

• Набор функций, обрабатывающих по заданным алгоритмам описанные в таблице откликов сооб­
щения Windows

Таблица откликов объявляется, как элемент класса главного окна с помощью макроса
DECLARE_RESPONSE_TABLE, с именем нашего конкретного класса в качестве параметра. При расши­
рении этого макроса в класс главного окна включается объявление указателя__ entries[] на массив струк­
турных пакетов данных, используемых для вызова конкретных функций отклика, а также прототип слу­
жебной функции Find(), с помощью которой осуществляется нахождение в таблице откликов элемента,
соответствующего пришедшему сообщению. Расширение указанного макроса содержится в заголовоч­
ном файле include\owl\eventhan.h, который наряду со многими другими файлами.h автоматически под­
ключается к компилируемому файлу приложения при выполнении директивы #include framewin.h. По­
скольку в расширении макроса присутствуют "собственные" спецификаторы доступа (private и public),
его можно располагать в любой секции класса, однако после этого макроса необходимо соответствую-


246 Глава 27

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

Таблица откликов может располагаться в любом месте программы. Она начинается с макроса DEFINE_RESPONSE_TABLE1 (описанного в том же файле eventhan.h), в качестве параметров которого указываются имена класса главного окна и родительского класса TFrameWindow. При расширении этого макроса в текст программы включается описание объявленной ранее функции Find(), а также строка, на­чинающая описание массива структурных пакетов данных для конкретных сообщений:

MyWindow::_____ entries[ ]={

Перечисляемые после этого через запятые условные имена обрабатываемых в приложении сообще­
ний, вроде EV_WM_TIMER или EV_WM_GETMINMAXINFO (которые, в свою очередь, являются мак­
росами), выступают в качестве элементов массива___ entries[]. Для обозначения конца этого массива ис­
пользуется макрос END_RESPONSE_TABLE, который расширяется в описание "пустого" структурного
пакета данных и завершающей фигурной скобки:

{0, 0, 0, 0}}

Описание любого массива должно, разумеется, завершаться знаком точки с запятой, которого, за­метьте, нет в расширении макроса END_RESPONSE_TABLE. Этот знак включаем в программу мы сами, указывая его после вызова этого макроса (см. текст примера 27.1).

В файле eventhan.h описаны четыре макроса вида DEFINE_RESPONSE_TABLEx, различающиеся по­следним символом-цифрой и предназначенные для использования в классах окон с различным числом родительских (базовых) классов, которые, в свою очередь, содержат внутренние таблицы отклика. Наш класс MyWindow является единственным потомком вертикальной (не разветвленной) структуры классов (см. рис. 25.4), поэтому мы использовали вариант макроса DEFINE_RESPONSE_TABLE 1. Если бы класс MyWindow происходил от двух или трех базовых классов, расположенных непосредственно над ним (и содержащих собственные таблицы отклика на те или иные сообщения), то для образования таблицы от­кликов пришлось бы использовать макросы DEFINE_RESPONSE_TABLE2 или DEFINE_RESPONSE_TABLE3. Для классов окон, не имеющих базовых, используется макрос DEFINE_RESPONSE_TABLE. Расширения указанной группы макросов различаются видом функции Find(), осуществляющей поиск в массиве структурных пакетов данных конкретного пакета, соответст­вующего пришедшему сообщению, что дает возможность в дальнейшем вызвать прикладную функцию обработки этого сообщения. Если для поступившего сообщения в нашей таблице отклика не нашлось со­ответствия, функция Find() пытается найти такое соответствие во всех базовых классах, имена которых мы предоставляем функции Find() через параметры использованной в программе разновидности макроса DEFINE_RESPONSE_TABLE. В нашем случае сообщения ищутся, кроме класса MyWindow, еще в базо­вом классе TFrameWindow.

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

Функция-диспетчер (все эти функции объявлены в файле \include\owl\dispach.h и описаны в файле \source\owl\dispatch.cpp) служит для преобразования параметров wParam и lParam поступившего сообще­ния в соответствующие типы данных и вызова конкретной функции обработки сообщения с передачей ей этих данных в качестве параметров. В обычной, процедурной программе Windows задачу выделения из параметров сообщения "значимых" данных, а также вызова прикладных функций обработки сообщений выполняет макрос HANDLE_MSG (см. гл. 9). При этом для каждой функции обработки того или иного - сообщения жестко определен свой состав и порядок параметров, однако имена этих функций, которые мы указываем в качестве параметров макросов HANDLE_MSG, могут быть любыми. В библиотеке же OWL имена функций обработки сообщений заранее вписаны в макрорасширения макросов, составляю­щих тело таблицы откликов, и произвольно выбирать их нельзя. Как видно из программы 27-1, содержи­мое таблицы откликов составляется из обозначений вида EV_WM_TIMER или EV_WM_GETMINMAXINFO, где к имени сообщения Windows прибавляется префикс EV_ (от event, со­бытие). Имена же функций обработки формируются из префикса Ev и имени сообщения (без префикса WM_), в котором все отдельные составляющие слова пишутся с прописной буквы (EvTimer, EvGetMin-Maxlnfo, EvGetFont, EvSysKeyUp и т.д.).

Так же, как и при процедурном программировании, заранее трудно угадать состав параметров каж­дой функции обработки сообщения. Перечень стандартных сообщений Windows с указанием формата их функций обработки можно найти в статье Standard Windows Messages интерактивного справочника, вхо­дящего в состав пакета Borland C++.

Обратимся теперь к тексту примера 27-1, который по сути не отличается от программы 19-3.

В классе главного окна MyWindow объявлены функции обработки сообщений WM_PAINT для пере­рисовывания окна, WM_CREATE для выполнения инициализирующих действий, конкретно, установки таймера Windows, WM_TIMER для смены содержимого окна каждую секунду и


Обработка сообщений Windows 247

WM_GETMINMAXINFO для придания окну фиксированных размеров. Как и в примерах предыдущей главы, обработка сообщения WM_PAINT выполняется путем замещения функции-заглушки Paint(); для обработки остальных сообщений предусмотрены записи в таблице откликов и соответствующие им функции откликов. В состав членов класса входят также поле szText для хранения текущей даты в сим­вольной форме, предложение с объявлением таблицы откликов (макрос DECLARE_RESPONSE_TABLE) и деструктор класса, который используется для уничтожения таймера, устанавливаемого при создании окна. Переменная szText, используемая только функцией-членом класса OutTime(), объявлена закрытой (private) в соответствии с канонами объектно-ориентированного программирования, требующими мак­симального скрытия данных. По тем же мотивам можно было объявить закрытыми и все функции откли­ка, чтобы у программиста не появилось искушение обратиться к ним не из функций-членов класса Му-Window, а из основной программы. В нашем учебном примере все эти рассуждения, разумеется, не име­ют никакого значения.

В конструкторе класса MyWindow с помощью структурной переменной Attr, являющейся элементом класса TWindow, устанавливаются начальные координаты и размеры окна, а также значение расширен­ного стиля окна WS_EX-TOPMOST, что заставляет наше окно всегда находиться на переднем плане. Размеры окна, будут в дальнейшем установлены в функции обработки сообщения WM_GETMINMAXlNFO, поэтому здесь для них указаны нулевые значения.

В функции обработки сообщения WM_CREATE устанавливается таймер с номером 1 и величиной временного интервала 1000 мс=1с и вызывается прикладная функция OutTime() для вывода в окно теку­щего времени, чтобы избежать вывода на экран в течение первой секунды после запуска приложения пустого окна.

Функция OutTime() была описана в гл. 19. В ней после получения текущего времени и преобразова­ния его в символьную форму вызывается функция-член класса TWindow Invalidate(), которая инициирует сообщение WM_PAINT и перерисовывание окна с новым значением времени. Заметьте, что здесь вызы­вается не функция API Windows InvalidateRect(HWND, RECT FAR*, BOOL), требующая указания деск­риптора перерисовываемого окна, области перерисовывания и значения флаги перекраски фона, а инкап­сулированная в классе TWindow функция Invalidate(bool erase=true), которая может вызываться вообще без параметров, если программиста устраивает задаваемое по умолчанию значение флага перекраски фо­на erase (фон перекрашивается автоматически).


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: