<< Назад Вернуться к списку

8. Схема перемещения сообщения в Windows. Остновные события, посылаемые окнам. Дополнительные параметры сообщений. План. 1. Очередь распределения сообщений. 2. Очередь сообщений приложения. 3. Функции предварительной обработки сообщения. 4. Обработчики сообщений. 5. Основные сообщения. 6. Дополнительные параметры сообщений. На одной из предыдущих лекций мы рассматривали обработку сообщений. Там, в частности, говорилось, что сообщения попадают в очередь, а затем по очереди обрабатываются. Но как они в эту очередь попадают? Нигде в нашей программе не было никакой реализации очереди и занесения в нее сообщений тоже не было. Было только взятие сообщения из нее функцией GetMessage. Дело в том, что всю работу по созданию и дополнению очереди сообщений берет на себя операционная система. Как же все это работает? Во первых, в Windows одновременно может работать несколько приложений. Каждому из них могут приходить сообщения, причем почти одновременно. И каждое приложение должно обрабатывать только свои сообщения. Поэтому для каждого приложения создается своя очередь сообщений. Во вторых, при возникновении сообщения, например, от клавиатуры, сообщение приходит не какому-то приложению, а операционной системе, и только она, определив какое приложение сейчас находится в активном состоянии, пересылает его в нужную очередь сообщений. Эти сообщения тоже могут приходить очень часто, поэтому существует также и глобальная очередь сообщений (или очередь распределения сообщений). Если же приходит сообщение от мыши, то при его распределении определяется, в окно какого приложения попал курсор, после чего, если это окно было активным, то сообщение просто переносится в нужную очередь, если же нет, то активному приложению передается сообщение о деактивации, неактивному - активизации, и только потом в его очередь добавляется пришедшие сообщение. Можно представить все это на схеме: очередь распределения сообщений --------------- Мышь --------------> | message1 | ---------- | message2 | --- | Клавиатура ----------> | message3 | | | | ... | | | ... -----------------> | | | | | | | | | | | | | | | | | | | | | | | | | | | | --------------- | | ---------------------- | | | | | | | Приложение1 | Приложение2 | --------------- | --------------- | | message2 | | | message1 | | | ... | | | ... | <--- | | <--- | | | | | | | | | | | | | | --------------- --------------- Далее в приложениях сообщения каким-то образом обрабатываются. Рассмотрим этот процесс подробнее: MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } MSG является структурой, имеющей следующее определение: struct MSG { HWND hwnd; // дескриптор окна - получателя сообщения UINT message; // идентификатор сообщения WPARAM wParam; // первый дополнительный параметр (целое число) LPARAM lParam; // второй дополнительный параметр (целое число) DWORD time; // время возникновения сообщения POINT pt; // координаты курсора в экране в момент }; // возникновения сообщения Приведу прототипы используемых здесь функций: BOOL GetMessage(LPMSG lpMsg, // адрес структуры MSG HWND hWnd, // дескриптор окна UINT wMsgFilterMin, // минимальное сообщение UINT wMsgFilterMax); // максимальное сообщение BOOL TranslateMessage(LPMSG lpMsg); LONG DispatchMessage(LPMST lpMsg); Функция GetMessage смотрит на очередь сообщений текущего приложения и ищет в ней первое сообщение, подходящее под требования, и, если находит, то записывает его в указанное первым параметром место, а из очереди удаляет. Требования определяются следующими тремя параметрами: hWnd - если он определен, то берутся только сообщения к этому окну, если этот параметр NULL - то просматриваются все сообщения wMsgFilterMin и wMsgFilterMax - определяют наименьший и соответственно наибольший идентификатор сообщения, который надо искать, если нули - то просматриваются все сообщения. Эта функция возвращает TRUE, если в идентификатор сообщения не был равен WM_QUIT. Если он равен WM_QUIT, то она возвращает FALSE, и происходит выход из цикла, после чего завершается и программа. Если на момент вызова функции GetMessage в очереди не было ни одного подходящего сообщения, то программа впадает в так назвыемую "спячку", ожидая его появления. Это необходимо для того, чтобы не расходовалось процессорное время на холостую прокрутку цикла (ведь одновременно с нашим сообщением могут быть запущены и другие, которые тоже хотят что-то делать). Как только сообщение в очереди появилось, оно передается в функция TranslateMessage, которая представляет собой преобразования виртуальных клавиш в сообщения клавиатуры с нормальными данными. Подробнее мы рассмотрим ее назначение позднее, пока же надо запомнить, что ее надо вызывать при получении нового сообщения перед его обработкой. Далее вызывается функция DispatchMessage, которая по дескриптору окна определяет класс окна, к которому оно принадлежит, а по классу определяет указатель на функцию обработки сообщений данного окна и вызывает эту функцию с соответствующими параметрами. В нашем случае вызывается функция WindowProc, а параметры, ей передаваемые, берутся из структуры msg. Теперь пришла пора внимательно рассмотреть функцию WindowProc. Эта функция не должна называться именно так. Главное, чтобы ее прототип был следующий: LRESULT CALLBACK FunctionName(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); Также надо не забыть при определении класса окон занести в него указатель на нее: winclass.lpfnWndProc = FunctionName; Что должна делать эта функция? Она должна для каждого типа сообщения что-то делать (ничего не делать - тоже что-то). После того, как она сделала все, что нужно, она должна возвращать 0 в случае успешной обработки сообщения и не 0 если случилось что-то плохое. Возвращенный 0 говорит операционной системе, что сообщение обработано и никаких дополнительных действий предпринимать не нужно. Если сообщение никак не обрабатывается, то можно вернуть не ноль, что иногда бывает полезным. Каким образом можно обрабатывать сообщения? Во-первых надо каким-то образом убедиться, что полученное сообщение именно того типа, что мы собираемся обрабатывать. Это можно сделать разными способами, но чаще всего используют оператор switch: switch (msg) { case WM_CREATE: return 0; case WM_PAINT: return 0; case WM_DESTROY: return 0; } В данном примере никаких действий при обработке сообщений WM_CREATE, WM_PAINT и WM_DESTROY не производится, однако оператор return 0 говорит, что сообщение обработано и обработано успешно. Теперь надо только вставить некоторые операторы перед ним и все. Но как я уже говорил, различных видов сообщений существует несколько сотен, неужели все их надо обрабатывать? Оказывается нет. Надо обрабатывать только те, реакция на которые должна отличаться от стандартной. Для остальных существует функция DefWindowProc, которая выполняет стандартные действия по обработке сообщений (в основном это ничего не делание). Ее прототип совпадает с прототипом нашей функции, поэтому она тоже может быть использована как обработчик сообщений. Таким образом, надо обработать только необходимые сообщения, а для остальных вызвать обработчик по умолчанию, как и сделано в нашей программе: LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_CREATE: return 0; case WM_PAINT: return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hWnd, msg, wParam, lParam); } Теперь приступим к описанию основных сообщений, получаемых окном. Эти сообщения мы будем чаще всего обрабатывать: WM_CREATE окно создается, но еще не существует WM_ACTIVATE окно активизируется или деактивизируется WM_CLOSE окно закрывается (нажали на кнопку закрытия) WM_DESTROY окно должно быть уничтожено WM_QUIT приложение завершается WM_MOVE окно перемещается WM_SIZE изменяются размеры окна WM_TIMER сообщение таймера WM_PAINT необходима перерисовка окна WM_KEYDOWN нажата кнопка клавиатуры WM_KEYUP отпущена кнопка клавиатуры WM_MOUSEMOVE перемещен курсор мыши WM_(R,M,L)BUTTON(DOWN,UP) нажата или отпущена кнопка мыши Рассмотрим первую группу сообщений. Это сообщения, касающиеся создания, активизации и закрытия окон. Первое сообщения, WM_CREATE, приходит перед созданием окна. При его обработке можно создать необходимые дочерние окна и проинициализировать другие объекты программы. Но т.к. окно еще не создано, то о его размерах и положении на экране ничего не известно. Дополнительных параметров у этого сообщения нет. WM_ACTIVATE приходит при активизации или деактивизации окна. Т.е. тогда, когда онко становится активным или когда перестает им быть. У этого сообщения есть дополнительные параметры. Это способ активизации, флаг минимизации и дескриптор предыдущего активного окна. Находятся они в wParam и lParam: fActive = LO

<< Назад Вернуться к списку

Хостинг от uCoz