- •Лабораторный практикум «Основы разработки приложений Windows» Книга 1
- •Часть 1. Теоретические сведения4
- •Часть 2. Лабораторный практикум95
- •Часть 1 Теоретические сведения
- •1. Структура приложения Windows Простейшая программа с главным окном
- •Структура программы
- •Главная функция WinMain()
- •Регистрация класса окна
- •Создание и показ окна
- •Сообщения Windows и цикл их обработки
- •Оконная функция
- •Макрос handle_msg и структурирование программы
- •2. Интерфейс графических устройств
- •Простая программа, использующая средства gdi
- •Обработка сообщений wmpaint
- •Контекст устройства
- •Использование графических инструментов
- •3. Ресурсы: меню Простая программа с меню
- •Файл ресурсов
- •Описание меню в файле ресурсов
- •Сообщение wmcommand
- •Программное создание меню
- •Плавающее меню
- •Инструментальная панель
- •Всплывающие подсказки
- •0,"Первая строка"
- •1,"Вторая строка"
- •2,"Третья строка"
- •4. Ресурсы: диалоговые окна
- •Простая программа с меню и диалогом
- •Описание диалога в файле ресурсов
- •Обслуживание модального диалога
- •Модальный диалог как главное окно приложения
- •Немодальный диалог
- •Список в диалоговом окне
- •Окно редактирования и статический элемент управления
- •Графика в диалоговом окне
- •Перекрашивание диалогового окна и его элементов
- •Часть 2 Лабораторный практикум Работы лабораторного практикума Работа 1. Вызов функций Windows
- •Работа 2. Главное окно приложения (пример 1-1 из настоящего пособия)
- •Работа 3. Вывод в главное окно приложения текста и фигур (пример 2-1 из настоящего пособия)
- •Работа 4.Вывод в главное окно приложения текста и фигур (индивидуальное задание а)
- •Работа 5. Меню и модальный диалог (пример 4-1 из настоящего пособия)
- •Работа 6.Меню (индивидуальное задание в)
- •Работа 7.Модальный диалог в качестве главного окна приложения (индивидуальное задание с)
- •Работа 8.Вывод графика в главное окно приложения (индивидуальное заданиеD)
- •Работа 9. Немодальный диалог
- •Работа 10. Диалог с окном редактирования
- •Работа 11. Программное создание меню
- •Работа 12. Плавающее меню
- •Работа 13. График в диалоговом окне
- •Работа 14. Инструментальная панель (tool bar)
- •Работа 15. Инструментальная панель со всплывающими подсказками (tool tips)
- •Работа 16. Пользовательские пиктограммы и курсоры
- •Работа 17. Локализация программных продуктов
- •Индивидуальные задания лабораторного практикума
Макрос 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); |