Московский инженерно-физический институт
Вид материала | Практикум |
СодержаниеМакрос HANDLE_MSG и структурированиепрограммы LRESULT CALLBACK WndProc(HWND hwnd,UINT msg |
- Ю. С. Барсуков 1, А. Ю. Окунев 2 1 Московский инженерно-физический институт (государственный, 29.25kb.
- В. А. Курнаев Московский инженерно-физический институт (государственный университет),, 27.18kb.
- «Вегето-сосудистая дистония», 192.12kb.
- Перечен ь научных разделов и базовых вузов по научным разделам открытого конкурса, 247.02kb.
- Д. В. Гуцко Московский инженерно-физический институт (государственный университет), 34.47kb.
- В. А. Тумольский московский инженерно-физический институт (государственный университет), 27.44kb.
- К. С. Чистов Московский инженерно-физический институт (государственный университет), 24.11kb.
- Вдокладе рассматривается задача оценки рисков инвестиционных проектов электростанций, 29.4kb.
- Резюме Луценко Владимир Юрьевич, 22.32kb.
- Л. Ю. Грецкая московский инженерно-физический институт (государственный университет), 26.28kb.
Макрос 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(). В документации к Windows рекомендуется образовывать имена функций обработки сообщений из имени класса окна, значка подчеркивания, слова On (при, в случае) и имени соответствующего сообщения. Применительно к нашей программе имена функций обработки сообщений должны выглядеть таким образом:
MainWindow_OnDestroy();
MainWindow_OnPaint();
MainWindow_OnCommand();
и т. д. Однако мы для функций обработки сообщений, поступающих в главное окно, будем ради краткости опускать префикс, характеризующий класс, и называть эти функции просто OnDestroy(), 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, HANDLEWMDESTROY и др. Таким образом, предложение
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 почти для каждого макроса HANDLEWMсообщение приведен прототип соответствующей функции с указанием типа, порядка и в какой-то степени смысла ее параметров. Более детальную информацию о данных, поступающих в приложение вместе с сообщением, можно получить с помощью интерактивного справочника среды программирования 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); |