- •Министерство образования и науки Российской Федерации федеральное агентство по образованию
- •Таганрог 2006
- •Лабораторная работа №1 исследование обмена сообщениями в windows
- •Механизм сообщений в Windows
- •Типы сообщений
- •Цикл приема сообщений
- •Оконные функции
- •Структура очереди сообщений
- •Асинхронная посылка сообщений
- •Синхронная посылка сообщений
- •Выполнение лабораторной работы
- •Пример выполнения задания
- •Варианты заданий
- •Контрольные вопросы
-
Выполнение лабораторной работы
Выполнение работы заключается в получении протокола сообщений, возникающих в ситуации, определенной вариантом задания.
Для получения протокола следует настроить Spy++ подходящим образом и осуществить заданные действия. Можно не отслеживать не оконные типы сообщений (не имеющие префикса WM_), сообщения, адресованные посторонним окнам и сообщения о перемещении мыши. Допускается также удаление сообщений, не упоминавшихся ни в описании данной работы, ни в описанном ниже примере выполнения. Все оставшиеся сообщения, следует прокомментировать и понимать, в ответ на какое действие пользователя появляется то или иное сообщение (или группа сообщений). Информацию о сообщениях Windows, их смысле и параметрах, можно найти в справочных данных системы программирования (Visual Studio, Delphi, C++ Builder) или, если есть возможность, в базе данных MSDN (Microsoft Developers Network). Ссылки на незнание английского языка не принимаются (пора знать).
Отчет о лабораторной работе оформляется на бумаге в печатном виде. На титульном листе указывается название работы и состав бригады. В отчете приводится формулировка задания и комментированный протокол сообщений согласно варианту задания.
Преподаватель, принимающий работу, проверяет отчет, а также задает вопросы по теме работы.
-
Пример выполнения задания
Рассмотрим выполнение лабораторной работы на следующем примере.
Задание. Получить и прокомментировать протокол сообщений, посылаемых окну редактора Notepad («Блокнот») при его закрытии. Окно закрывать щелчком по кнопке закрытия. Считать, что текст в окне редактора сохранен, т.е. запрос на сохранение выдаваться не будет.
Пример выполнялся в системе Windows XT. Для других версий Windows поток сообщений может несколько отличаться.
Сначала запустим Notepad, введем несколько строк текста и сохраним файл. Затем запустим Spy++ и выполним команду Spy+Log Messages. Откроется диалоговое окно Message Options. На закладке Windows оттащим Finder Tool на окно Notepad. При этом можно обнаружить, что Notepad содержит, как минимум, два окна: основное окно программы и вложенное в него (дочернее) окно с панелью редактора (на самом деле, имеется еще окно строки состояния, которое может быть видимым или нет, в зависимости от настройки Notepad). Выберем главное окно, но включим переключатель Child Windows, чтобы получать сообщения для всех окон Notepad. На закладке Messages пока оставим все сообщения.
Полезно перейти теперь в окно списка окон (через команду меню Spy++ Windows), на всякий случай обновить список (командой контекстного меню Refresh) и найти в списке окна Notepad. Это можно сделать либо просмотром всего списка, либо с помощью команды Search+Find Window. В нашем случае список содержит главное окно Notepad с хэндлом 002702D2, дочернее окно редактирования (хэндл 001103В0) и окно строки состояния (001002B4). Разумеется, значения хэндлов при других запусках Notepad могут быть другими.
Вернемся в окно протокола сообщений. Чтобы получить меньше ненужных сообщений, желательно расположить на экране окна таким образом, чтобы кнопка закрытия Notepad была как можно ближе к краю окна Spy++. Убедимся, что протоколирование включено (ненадолго поместим курсор на Notepad, при этом в окне Spy++ должен политься бурный поток сообщений). Удалим все сообщения командой Messages+Clear Log.
Теперь за дело: быстро переведем курсор из окна Spy++ на кнопку закрытия Notepad и щелкнем левой кнопкой мыши. Дальше можно не спешить, поскольку после закрытия Notepad поток сообщений так или иначе прекратится.
В данном случае в протокол успело попасть 170 сообщений. Сохраним их в файле для редактирования. После удаления малоинформативных, часто повторяющихся, сообщений останется протокол, приведенный в таблице ниже.
<00001> 002702D2 S |
WM_NCHITTEST xPos:258 yPos:191 |
<00002> 002702D2 R |
WM_NCHITTEST nHittest:HTCLOSE |
<00003> 002702D2 S |
WM_SETCURSOR hwnd:002702D2 nHittest:HTCLOSE wMouseMsg:WM_MOUSEMOVE |
<00004> 002702D2 R |
WM_SETCURSOR fHaltProcessing:False |
<00005> 002702D2 P |
WM_NCMOUSEMOVE nHittest:HTCLOSE xPos:258 yPos:191 |
<00006> 001103D0 S |
EM_GETSEL lpdwStart:0007FC74 lpdwEnd:0007FC78 |
<00007> 001103D0 R |
EM_GETSEL wStart:0 wEnd:0 lpdwStart:0007FC74 (0) lpdwEnd:0007FC78 (0) |
<00008> 001103D0 S |
EM_LINEFROMCHAR ich:0 |
<00009> 001103D0 R |
EM_LINEFROMCHAR iLine:0 |
<00010> 001103D0 S |
EM_LINEINDEX line:0 |
<00011> 001103D0 R |
EM_LINEINDEX ich:0 |
............ |
.................. |
<00094> 002702D2 S |
WM_NCHITTEST xPos:256 yPos:185 |
<00095> 002702D2 R |
WM_NCHITTEST nHittest:HTCLOSE |
<00096> 002702D2 S |
WM_MOUSEACTIVATE hwndTopLevel:002702D2 nHittest:HTCLOSE uMsg:WM_LBUTTONDOWN |
<00097> 002702D2 R |
WM_MOUSEACTIVATE fuActivate:MA_ACTIVATE |
<00098> 002702D2 S |
WM_WINDOWPOSCHANGING lpwp:0007FE98 |
<00099> 002702D2 R |
WM_WINDOWPOSCHANGING |
<00100> 002702D2 S |
WM_NCPAINT hrgn:00000001 |
<00101> 002702D2 S |
WM_GETTEXT cchTextMax:510 lpszText:0007F45C |
<00102> 002702D2 R |
WM_GETTEXT cchCopied:29 lpszText:0007F45C ("@8<5@ ") |
<00103> 002702D2 R |
WM_NCPAINT |
<00104> 002702D2 S |
WM_ERASEBKGND hdc:67010BA7 |
<00105> 002702D2 R |
WM_ERASEBKGND fErased:True |
<00106> 002702D2 S |
WM_WINDOWPOSCHANGED lpwp:0007FE98 |
<00107> 002702D2 R |
WM_WINDOWPOSCHANGED |
<00108> 002702D2 S |
WM_ACTIVATEAPP fActive:True dwThreadID:00000000 |
<00109> 002702D2 R |
WM_ACTIVATEAPP |
<00110> 002702D2 S |
WM_NCACTIVATE fActive:True |
<00111> 002702D2 R |
WM_NCACTIVATE |
<00112> 002702D2 S |
WM_ACTIVATE fActive:WA_ACTIVE fMinimized:False hwndPrevious:(null) |
<00113> 002702D2 S |
WM_SETFOCUS hwndLoseFocus:(null) |
<00114> 002702D2 S |
WM_KILLFOCUS hwndGetFocus:001103D0 |
<00115> 001103D0 S |
WM_KILLFOCUS hwndGetFocus:001103D0 |
<00116> 002702D2 S |
WM_COMMAND wNotifyCode:EN_KILLFOCUS wID:15 hwndCtl:001103D0 |
<00117> 002702D2 R |
WM_COMMAND |
<00118> 001103D0 R |
WM_KILLFOCUS |
<00119> 002702D2 R |
WM_KILLFOCUS |
<00120> 001103D0 S |
WM_SETFOCUS hwndLoseFocus:002702D2 |
<00121> 002702D2 S |
WM_CTLCOLOREDIT hdcEdit:67010BA7 hwndEdit:001103D0 |
<00122> 002702D2 R |
WM_CTLCOLOREDIT hBrush:0110007B |
<00123> 002702D2 S |
WM_COMMAND wNotifyCode:EN_SETFOCUS wID:15 hwndCtl:001103D0 |
<00124> 002702D2 R |
WM_COMMAND |
<00125> 001103D0 R |
WM_SETFOCUS |
<00126> 002702D2 R |
WM_SETFOCUS |
<00127> 002702D2 R |
WM_ACTIVATE |
<00128> 002702D2 S |
WM_SETCURSOR hwnd:002702D2 nHittest:HTCLOSE wMouseMsg:WM_LBUTTONDOWN |
<00129> 002702D2 R |
WM_SETCURSOR fHaltProcessing:False |
<00130> 002702D2 P |
WM_NCLBUTTONDOWN nHittest:HTCLOSE xPos:256 yPos:185 |
<00131> 001103D0 S |
EM_GETSEL lpdwStart:0007FABC lpdwEnd:0007FAC0 |
<00132> 001103D0 R |
EM_GETSEL wStart:0 wEnd:0 lpdwStart:0007FABC (0) lpdwEnd:0007FAC0 (0) |
<00133> 001103D0 S |
EM_LINEFROMCHAR ich:0 |
<00134> 001103D0 R |
EM_LINEFROMCHAR iLine:0 |
<00135> 001103D0 S |
EM_LINEINDEX line:0 |
<00136> 001103D0 R |
EM_LINEINDEX ich:0 |
<00137> 002702D2 S |
WM_NCHITTEST xPos:256 yPos:185 |
<00138> 002702D2 R |
WM_NCHITTEST nHittest:HTCLOSE |
<00139> 002702D2 P |
WM_MOUSEMOVE fwKeys:MK_LBUTTON xPos:249 yPos:-29 |
<00140> 002702D2 P |
WM_LBUTTONUP fwKeys:0000 xPos:249 yPos:-29 |
<00141> 002702D2 S |
WM_CAPTURECHANGED hwndNewCapture:00000000 |
<00142> 002702D2 R |
WM_CAPTURECHANGED |
<00143> 002702D2 S |
WM_SYSCOMMAND uCmdType:SC_CLOSE xPos:256 yPos:185 |
<00144> 002702D2 S |
WM_CLOSE |
<00145> 001103D0 S |
EM_GETMODIFY |
<00146> 001103D0 R |
EM_GETMODIFY fModified:False |
<00147> 002702D2 S |
WM_PARENTNOTIFY fwEvent:WM_DESTROY idChild:0401 (1025) hwndChild:001002B4 |
<00148> 002702D2 R |
WM_PARENTNOTIFY |
<00149> 001002B4 S |
WM_SHOWWINDOW fShow:False fuStatus:0 (ShowWindow was called) |
<00150> 002702D2 S |
WM_ERASEBKGND hdc:C8010B4C |
<00151> 002702D2 R |
WM_ERASEBKGND fErased:True |
<00152> 002702D2 S |
WM_WINDOWPOSCHANGING lpwp:0007F978 |
<00153> 002702D2 R |
WM_WINDOWPOSCHANGING |
<00154> 002702D2 S |
WM_WINDOWPOSCHANGED lpwp:0007F978 |
<00155> 002702D2 R |
WM_WINDOWPOSCHANGED |
<00156> 002702D2 S |
WM_NCACTIVATE fActive:False |
<00157> 002702D2 R |
WM_NCACTIVATE fDeactivateOK:True |
<00158> 002702D2 S |
WM_ACTIVATE fActive:WA_INACTIVE fMinimized:False hwndPrevious:(null) |
<00159> 002702D2 R |
WM_ACTIVATE |
<00160> 002702D2 S |
WM_ACTIVATEAPP fActive:False dwThreadID:00000D20 |
<00161> 001103D0 S |
EM_GETSEL lpdwStart:0007F78C lpdwEnd:0007F790 |
<00162> 002702D2 R |
WM_ACTIVATEAPP |
<00163> 002702D2 S |
WM_COMMAND wNotifyCode:EN_KILLFOCUS wID:15 hwndCtl:001103D0 |
<00164> 002702D2 R |
WM_COMMAND |
<00165> 002702D2 S |
WM_DESTROY |
<00166> 002702D2 R |
WM_DESTROY |
<00167> 002702D2 S |
WM_NCDESTROY |
<00168> 002702D2 R |
WM_NCDESTROY |
<00169> 002702D2 R |
WM_CLOSE |
<00170> 002702D2 R |
WM_SYSCOMMAND |
Прокомментируем протокол.
Строки 1-2. Система запрашивает у окна, на какой части неклиентской области находится курсор. И сама себе отвечает: он на кнопке закрытия (HTCLOSE).
3-4. Система предлагает окну сменить, если надо, форму курсора.
5. Асинхронное сообщение о перемещении мыши. Чаще всего оно не обрабатывается.
6-7. У дочернего окна редактирования запрашивают позицию начала и конца выделенного в окне текста (например, чтобы отобразить его другими цветами). Ответ: 0, 0 (нет выделенного текста).
8-9. Запрос номера строки, где начинается выделение. Ответ: 0.
10-11. Теперь запрашивается номер позиции в этой строке. Тоже 0.
Далее многократно повторяется та же группа сообщений, с единственным отличием – небольшими изменениями позиции мыши (xPos, yPos). Это система перепроверяет положение курсора, пока он ползет пару миллиметров по кнопке закрытия.
94-95. Еще раз проверяется положение мыши. Она все еще на кнопке закрытия окна.
96-97. Это уже интереснее. Система сообщает окну, что пользователь пытается его активизировать. И подробности: был щелчок левой кнопкой мыши на кнопке закрытия окна. Указывается также хэндл окна верхнего уровня, которое должно принимать решение, как реагировать на щелчок. Ответ: можно активизировать, окно не возражает; и, кроме того, щелчок надо передать кнопке закрытия для обработки (если бы ответ был MA_ACTIVATEANDEAT, окно бы активизировалось, но щелчок был бы «съеден», т.е. не привел бы к закрытию окна).
98-99. Окну сообщают, что его положение на экране будет изменено. Детали предстоящего изменения оконная функция может найти по указанному адресу (lpwp). Ответ не требуется, но сообщение принято к сведению.
100. Окну сообщают, что надо перерисовать неклиентскую область. Оконная функция передает это сообщение системе и та принимается за дело (см. вложенные сообщения 101-103).
101-102. Система запрашивает у программы текст заголовка окна (потому что его надо отобразить заново на синем фоне). При этом cchTextMax – размер системного буфера для приема строки текста, а lpszText – адрес этого буфера. В ответ выдается 29 символов текста, и Spy++ безуспешно пытается отобразить первое слово русского заголовка.
103. Неклиентская часть перерисована.
104-105. В связи с предстоящей перерисовкой окна требуется очистить его фон. Сделано.
106-107. Изменение положения окна завершено (в данном случае окно Notepad размещено поверх прочих окон Windows).
108-109. Окну сообщают, что программа Notepad активизируется.
110-111. Окно активизируется и, возможно, его неклиентская часть требует при этом перерисовки.
112. Окну сообщают, что оно становится активным. Сообщения 113-127 посылаются при обработке сообщения 112.
113-119. Идет малопонятная игра с передачей фокуса клавиатурного ввода сначала главному окну Notepad, затем дочернему окну редактирования, а затем этим окном самому себе! В сообщении 116 редактор даже успевает сообщить главному окну, что теряет фокус.
120. Все-таки фокус переходит к окну редактирования.
121-122. Редактор просит родителя указать, каким цветом перерисовать фон (в связи с получением фокуса). В ответ он получает хэндл соответствующей кисти.
123. Редактор радостно сообщает родителю, что фокус он в конце концов получил.
124-127. И на этом заканчивается обработка активизации окна Notepad.
128-138. Еще раз проверяется положение мыши, возможность смены курсора и границы селектированного текста. На этот раз это все делается не в связи с перемещением мыши, а вследствие нажатия левой кнопки (WM_LBUTTONDOWN).
139-140. Окно Notepad получает асинхронные извещения о том, что после щелчка курсор еще немного передвинулся, а затем левая кнопка была отпущена. Системе не жалко известить, вдруг эти детали играют какую-то роль для окна. Отрицательная координата yPos не должна удивлять, поскольку координаты здесь отсчитываются вниз/вправо от левого верхнего угла клиентской части окна.
141-142. Окну сообщают, что оно потеряло захват мыши.
143. Окно получает системную команду закрытия (это прямой результат щелчка на кнопке закрытия). Все дальнейшие сообщения посылаются в порядке выполнения этой команды.
144. Система говорит окну: «Закройся!». Далее идет выполнение закрытия.
145-146. У окна редактора спрашивают, не изменен ли в нем текст (т.е. требуется ли запрос о сохранении). Ответ: «Нет, не изменен».
147-148. Наконец-то как-то проявилось окно строки состояния. Оно сообщает родителю, что прекращает свое существование.
149. Окно строки состояния получает команду не отображаться.
150-151. Зачем-то вытирается главное окно.
152-155. Окно меняет свое положение (теперь оно «никакое»).
156-157. Окну сообщают, что неклиентская часть должна быть показана неактивной. Окно не возражает, чего уж там…
158-164. Окно и приложение деактивируются, редактор теряет фокус.
165-166. Окно Notepad и его дочерние окна полностью разрушаются.
167-168. Самое последнее: система освобождает память, отведенную для структур данных окна.
169-170. И на этом заканчивается обработка сообщения 144 и породившего его сообщения 143.