Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лабораторный практикум «Основы разработки приложений Windows» книга 1.DOC
Скачиваний:
107
Добавлен:
10.05.2014
Размер:
721.41 Кб
Скачать

Макрос handle_msg и структурирование программы

Как было показано в предыдущем разделе, оконная функция должна представлять собой длинную конструкцию с оператором switchи со столькими блокамиcase, сколько сообщений Windows предполагается обрабатывать в программе. При обработке ряда сообщений, напримерWMCOMMAND, внутрь блоковcaseприходится включать вложенные операторыswitch-case, да еще не одного уровня вложенности. В результате функцияWndProc()становится чрезвычайно длинной и запутанной. Фактически все приложение оказывается состоящим из едва ли не единственной функцииWndProc()со множеством разветвлений внутри.

Заметного упрощения структуры программы можно добиться, используя группу макросов HANDLEMSG, определенных в файлеWINDOWSX.H. При использовании этих макросов все процедуры обработки сообщений выделяются вотдельные функции, а в оконной функцииWndProc()остаются только строки с макросамиHANDLEMSG(по числу обрабатываемых в программе сообщений), которые обеспечивают передачу управления на эти функции при приходе того или иного сообщения. Оконная функция, даже при большом количестве обрабатываемых сообщений, становится короткой и чрезвычайно наглядной; наличие же для обработки каждого сообщения отдельной прикладной функции также весьма упрощает разработку и отладку их алгоритмов.

Модифицируем программу 1-1, введя в ее оконную функцию макросHANDLEMSG.Фактически изменению подвергнется только оконная функция, которая распадется на две: собственно оконную функцию и вынесенный в отдельную функцию(названную намиOnDestroy()) алгоритм обработки сообщенияWMDESTROY. Ниже приведены эти изменения.

/*Пример 1-1а (фрагменты). /*Оконная функция на основе макросаHANDLE_MSG*/

/*Прототипы используемых в программе функций пользователя*/

LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);

void OnDestroy(HWND);

/*Оконная функция WndProcглавного окна*/

LRESULT CALLBACK WndProc(HWND hwnd,UINT msg,

WPARAM wParam,LPARAM lParam){

switch(msg){

HANDLE_MSG(hwnd,WM_DESTROY,OnDestroy);

default:

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

}

}

/*Функция OnDestroyобработки сообщенияWM_DESTROY*/

void OnDestroy(HWND){

PostQuitMessage(0);//Вызов функции завершения приложения

}

В нашей программе обрабатывается единственное сообщение WMDESTROY. Соответственно в программу введена функция обработки этого сообщенияOnDestroy(). В документации к Win­dows рекомендуется образовывать имена функций обработки сообщений из имени класса окна, значка подчеркивания, словаOn(при, в случае) и имени соответствующего сообщения. Применительно к нашей программе имена функций обработки сообщений должны выглядеть таким образом:

MainWindow_OnDestroy();

MainWindow_OnPaint();

MainWindow_OnCommand();

и т. д. Однако мы для функций обработки сообщений, поступающих вглавноеокно, будем ради краткости опускать префикс, характеризующий класс, и называть эти функции простоOnDest­roy(), OnPaint() и т. д. Для функций, относящихся к внутренним окнам, префикс придется использовать, так как разные функции, разумеется, должны иметь разные имена.

Введение в программу новой функции требует определения ее прототипа. Соответственно в раздел прототипов приложения включена строка

void OnDestroy(HWND);//Прототип функции OnDestroy()

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

Макрос HANDLEMSG, коротко говоря, разворачивается в предложение языка C++ с ключевым словомcase. Общий же для всех ключевых словcaseоператорswitchвключается в текст оконной функции в явной форме:

LRESULT CALLBACK WndProc(HWND hwnd,UINT msg,

WPARAM wParam,LPARAM lParam){

switch(msg){

HANDLE_MSG(hwnd,WM_PAINT,OnPaint);/*Фактически

case WM_PAINT: OnPaint()*/

HANDLE_MSG(hwnd,WM_DESTROY,OnDestroy;/*Фактически

case WM_DESTROY: OnDestroy()*/

В действительности, однако, расширение HANDLEMSGоказывается не таким простым.

Для макроса HANDLE_MSGв составе файлаWINDOWSX.Hимеется следующее определение:

#define HANDLE_MSG(hwnd,message,fn)\

case(message):return HANDLE_##message\

((hwnd),(wParam),(lParam),(fn))

(знак обратной косой черты (\) в макроопределении служит для перехода на следующую строку и никак не влияет на макрорасширение). Знак##в составе макроопределения обозначает сцепление (конкатенацию) и в данном случае служит для получения составных имен новых макросовHANDLEWMPAINT,HANDLEWMDE­STROYи др. Таким образом, предложение

HANDLE_MSG(hwnd,WM_PAINT,OnPaint);

преобразуется в промежуточное макрорасширение

case (WM_PAINT):return HANDLE_WM_PAINT((hwnd),

(wParam),(lParam),(OnPaint));

а предложение

HANDLE_MSG(hwnd,WM_DESTROY,OnDestroy;

преобразуется в макрорасширение

case (WM_DESTROY):return HANDLE_WM_DESTROY((hwnd),

(wParam),(lParam),(OnDestroy));

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

Для макроса HANDLEWMDESTROYдано следующее макроопределение:

#define HANDLE_WM_DESTROY(hwnd,wParam,lParam,fn)\

((fn)(hwnd),0L)

Подставив это определение вместо HANDLEWMDESTROYи опустив ненужные скобки, получим окончательное макрорасширение:

case WM_ DESTROY:return (OnDestroy (hwnd),0L);

Таким образом, по ходу расширения макроса убрались лишние (для данного сообщения) параметры wParamиlParamи образовалось синтаксически правильное предложениеcase. Как выполняется это предложение? Если пришло сообщениеWMDESTROY,выполняется операторreturnс двумя аргументами. Прежде всего выполняется оператор, стоящий на месте первого аргумента, т. е. вызывается функцияOnDestroy (hwnd). Эта функция не должна возвращать каких-либо значений. После ее завершения срабатывает операторreturn, возвращающий указанное значение – длинный 0. Любопытно, что завершающий знак“;”, который обязательно должен быть в конце любого предложения языка C++, переходит в окончательный текст изнашейстроки с макросомHANDLEMSG.

Схожим образом расширяется строка для сообщения, например, WMPAINT:

case WM_PAINT:return(OnPaint(hwnd),0L);

Для других сообщений макросы вида HANDLEсообщениеимеют более сложные определения, в которых выполняются необходимые преобразования аргументов функцииWndProc() wParam иlParam в параметры функций обработки сообщений.

Таким образом, параметры прикладных функций обработки сообщений определяются макросами HANDLEWMсообщение.Однако извлечь интересующую нас информацию о прототипе функции из текста макроса затруднительно. Для облегчения программирования в файлеWINDOWSX.Hпочти для каждого макросаHANDLE­WMсообщениеприведен прототип соответствующей функции с указанием типа, порядка и в какой-то степени смысла ее параметров. Более детальную информацию о данных, поступающих в приложение вместе с сообщением, можно получить с помощью интерактивного справочника среды программирования Borland C++, вызвав справку по интересующему нас сообщению (например,WMDESTROY). Таким образом, при написании функций обработки сообщений приходится постоянно обращаться к файлуWINDOWSX.Hи справочной системе среды разработки.

В табл. 1.1 приведены прототипы некоторых наиболее употребительных функций обработки сообщений; имена функций образованы из слова On и имени сообщения (в реальной программе эти имена могут быть какими угодно).

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

Таблица 1.1. Прототипы функций обработки сообщений

Сообщение

Прототип функции

WM_CHAR

void OnChar(HWND hwnd, UINT ch, int cRepeat);

WM_COMMAND

void OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify);

WM_CREATE

BOOL OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct);

WM_CTLCOLORDLG

HBRUSH OnCtlColorDlg(HWND hwndDlg, HDC hdcDlg, HWND hwndDlg, int code)

WM_CTLCOLORSTATIC

HBRUSH OnCtlColorStatic(HWND hwnd, HDC hdcDlg, HWND hwndCtl, int code)

WM_DESTROY

void OnDestroy(HWND hwnd);

WM_GETMINMAXINFO

void OnGetMinMaxInfo( HWND hwnd, LPMINMAXINFO lpMinMaxInfo);

WM_INITDIALOG

BOOL OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam);

WM_LBUTTONDOWN

void OnLButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags);

WM_MOUSEMOVE

void OnMouseMove(HWND hwnd, int x, int y, UINT keyFlags);

WM_NOTIFY

BOOL OnNotify(HWND hwnd, INT idCtrl, NMHDR* pnmh);

WM_PAINT

void OnPaint(HWND hwnd);

WM_RBUTTONDOWN

void OnRButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags);

WM_SYSCOMMAND

void OnSysCommand(HWND hwnd, UINT cmd, int x, int y);

WM_TIMER

void OnTimer(HWND hwnd, UINT id);