The Russian Electronic Developer Magazine | |
Русский электронный журнал разработчика | |
Щелкни кобылу в нос - она махнет хвостом.
Козьма Прутков
Замечание. | То, о чем пойдет речь в этой статье, скорее всего, хорошо известно. Используемые приемы давно описаны в литературе. Печально, что никто так и не собрался с силами все это связно изложить, так чтобы понятно было даже начинающим :-((. Или просто мне не попадалось подобное описание? |
Чем больше пишешь программ под OS/2 (речь идет о Presentation Manager и, естественно, WPS), тем больше раздражает то, что достаточно часто упираешься в ситуацию, которую лучше всего описывает фраза, вынесенная в эпиграф. Махнуть-то она, конечно, махнет, но в каком направлении и с какой силой - известно далеко не всегда :-))
По большому счету, все работает. И даже в соответствии с документацией :-)), но... Кто не сталкивался с намертво заклинившим WPS, когда отладчик встает на точку останова, установленную внутри обработчика какого-нибудь сообщения, например, WM_PAINT? Или с трапами приложения (а то и всей системы), когда в обработчике какого-нибудь сообщения вызываешь функцию API и вдруг оказывается, что эта функция посылает на обработку это же самое сообщение, да еще и методом SEND?
Опыт приходит постепенно и начинаешь понимать, что в отладчике не стОит ставить точки останова после получения пространства отображения (presentation space), что перед использованием даже давно знакомой функции API не помешает еще раз залезть в документацию и прочитать, как и что она делает, что таймер и владельца клипборда лучше назначать в отдельные невидимые окна нулевого размера, и т.д.
Однако, все равно, остается чувство некоторой неполноценности документации и литературы (доступной, естественно) потому, что нигде не удается найти хотя бы частичное описание того, как связаны между собой различные сообщения. Например, какие сообщения и в какой последовательсти поступят на обработку после сообщения WM_CREATE, если обработчик этого сообщения вернет значение FALSE? А если TRUE? Или какие сообщения отправит на обработку функция WinDefWindowProc() в каждом конкретном случае?
Ответить на эти вопросы можно лишь с помощью трассировки сообщений. Но чем ее выполнять? Какие инструменты существуют для этого?
Отладчик для этих целей слишком груб. Кроме того, использование отладчика, хотим мы того или нет, существенно искажает общую картину поведения окна приложения, а значит и трассу сообщений. Например, пусть наше приложение запущено под отладчиком и точка останова установлена внутри какой-нибудь функции приложения, вызываемой из обработчика некоего пользовательского сообщения (как правило, это вполне безопасно и не вызывает повисания WPS и трапов). И пусть это пользовательское сообщение генерируется по нажатию функциональной клавиши F4, например. Тогда приложение будет нормально выполняться под отладчиком до тех пор, пока не будет нажата клавиша F4. Налетев на точку останова в соответствующей функции, отладчик бодро активизирует свои окна и покажет нам состояние приложения на момент останова. Но, если до останова активным было окно приложения, то после активным стало окно отладчика, а окно приложения уже успело получить и, возможно, обработать сообщение-уведомление о смене фокуса и деактивации, по крайней мере! И, если работа перехваченной функции зависит от состояния окна, то результаты ее дальнейшего выполнения будут искажены.
Использовать какие-либо внешние трассировщики сообщений тоже не очень удобно. Ну, во-первых, они как-то не попадались, а во-вторых, интересно ведь посмотреть не только какие сообщения попадают в очередь, но и коды возврата обработчиков этих сообщений, а также каким образом были получены эти коды: вернул ли их непосредственно обработчик сообщения или код был получен в результате выполнения функции WinDefWindowProc(), а эти вещи внешним трассировщиком посмотреть вряд ли удастся.
Более приемлемым представляется вариант встраивания трассировки непосредственно в приложение. Но... стряпать для этих целей отдельное окно, организовывать в него вывод сообщений, да еще и вывод в файл необходим... "Хлопотно это" (С) товарищ Сухов. Да и потом непонятно, как это дополнительное окно будет влиять на поведение основных окон приложения и, соответственно, на трассу сообщений. Гораздо проще было бы, если бы PM-приложения могли осуществлять вывод на устройство стандартного вывода stdout (если б оно у них было).
А ведь уже достаточно давно известно, как заставить VIO-приложение прикинуться PM-ным. И получается некий двухголовый монстр, который может и PM-ные окна использовать, и VIO-окно в качестве stdout. Правда, в полноэкранной сессии это работать не будет. По крайней мере, мне такая информация попала в самом начале 1998 года от Evgeny Kotsuba (спасибо ему :-)), который перекинул мне следующее сообщение от Serge Ivanov (и ему тоже спасибо! :-))) из FIDO:
From: Serge Ivanov <Serge.Ivanov@p200.f7.n5000.z2.fidonet.org> Я не увеpен, что это будет pаботать в стаpых пополамах (у меня Warp 4), хотя скоpее всего да. Кто скажет, что знал об этом с детства, пусть бpосит в меня камень. Hайти пpименение этому, я думаю, пpоблем не составляет, начиная от отладки, и кончая созданием смешанных пpиложений. --- #define INCL_WIN #define INCL_PM #define INCL_DOSPROCESS #include <os2.h> #include <stdio.h> void main(void) { HAB hab; HMQ hmq; PPIB pib; PTIB tib; printf("In console\n"); DosGetInfoBlocks(&tib, &pib); /* Try morphing into a PM application. */ if (pib->pib_ultype == 2) /* VIO */ pib->pib_ultype = 3; hab = WinInitialize(0); hmq = WinCreateMsgQueue(hab, 0); if (!hmq) { printf("Cannot create a message queue\n"); return; } WinMessageBox(HWND_DESKTOP, 0, "Now PM application", "Morphing", 100, MB_OK); printf("Still pm app\n"); WinMessageBox(HWND_DESKTOP, 0, "Still PM application", "Morphing", 100, MB_OK); printf("Still pm app\n"); WinDestroyMsgQueue(hmq); WinTerminate(hab); printf("All done\n"); }И тут внезапно все стало просто. В качестве подопытных кроликов были выбраны каноническая hello из IBM Developer's Toolkit for OS/2 и БГ(Болван диалоГовый). После соответствующих переделок эти PM-приложения начали бодро выдавать трассу сообщений в VIO-окно, как на устройство стандартного вывода stdout. Причем это полноценный stdout - вывод в него можно перенаправить в файл и т.д.
Вот пример трассы сообщений, создавамый программой hello, которая была запущена и тут же завершена нажатием горячей клавиши F3:
Msg=WM_CREATE RC=FALSE Msg=WM_ADJUSTWINDOWPOS RC=(*)0x0 Msg=WM_FORMATFRAME RC=(*)0x0 (count of SWP) Msg=WM_WINDOWPOSCHANGED Msg=WM_SIZE RC=(*)0x0 (reserved) Msg=WM_SHOW RC=(*)0x0 (reserved) RC=(*)0x0 (reserved) Msg=WM_QUERYFOCUSCHAIN Mp1=QFC_ACTIVE RC=(*)0x800001DA Msg=WM_QUERYFOCUSCHAIN Mp1=QFC_PARTOFCHAIN Msg=WM_QUERYFOCUSCHAIN Mp1=QFC_NEXTINCHAIN RC=(*)0x800001DA RC=(*)0x0 Msg=WM_FOCUSCHANGE Msg=WM_QUERYFOCUSCHAIN Mp1=QFC_FRAME RC=(*)0x800001DA Msg=WM_QUERYDLGCODE RC=(*)0x0 Msg=WM_QUERYFOCUSCHAIN Mp1=QFC_FRAME RC=(*)0x800001DA Msg=WM_ERASEBACKGROUND RC=TRUE Msg=WM_ACTIVATE Mp1=TRUE Mp2=0x800001DA (HWND) RC=(*)0x0 (reserved) Msg=WM_SETSELECTION RC=(*)0x0 (reserved) Msg=WM_SETFOCUS RC=(*)0x0 (reserved) RC=(*)0x0 (reserved) Msg=0xE RC=(*)0x0 Msg=0xBD4 RC=(*)0x0 Msg=0xBD4 RC=(*)0x0 Msg=WM_PAINT RC=0x0 (reserved) Msg=WM_TRANSLATEACCEL RC=(*)FALSE Msg=WM_CHAR RC=(*)FALSE Msg=WM_TRANSLATEACCEL Msg=WM_INITMENU Mp1=FID_MENU RC=(*)0x0 (reserved) Msg=WM_MENUEND Mp1=FID_MENU RC=(*)0x0 (reserved) RC=(*)TRUE Msg=WM_COMMAND RC=0x0 (reserved) Msg=WM_CLOSE RC=0x0 (reserved) Msg=WM_QUIT (!!!) RC=0 (reserved) Msg=WM_SAVEAPPLICATION RC=(*)0x0 (reserved) Msg=WM_FOCUSCHANGE Msg=WM_QUERYFOCUSCHAIN Mp1=QFC_FRAME RC=(*)0x800001DA Msg=WM_SETFOCUS RC=(*)0x0 (reserved) Msg=WM_SETSELECTION RC=(*)0x0 (reserved) Msg=WM_ACTIVATE Mp1=FALSE Mp2=0x800001DA (HWND) RC=(*)0x0 (reserved) RC=(*)0x0 (reserved) Msg=WM_QUERYFOCUSCHAIN Mp1=QFC_ACTIVE RC=(*)0x800001DA Msg=WM_QUERYFOCUSCHAIN Mp1=QFC_PARTOFCHAIN Msg=WM_QUERYFOCUSCHAIN Mp1=QFC_NEXTINCHAIN RC=(*)0x800001DA RC=(*)0x0 Msg=0xE RC=(*)0x0 Msg=WM_ADJUSTWINDOWPOS RC=(*)0x0 Msg=WM_WINDOWPOSCHANGED Msg=WM_SHOW RC=(*)0x0 (reserved) RC=(*)0x0 (reserved) Msg=WM_DESTROY RC=(*)0x0 (reserved)Обозначения:
Кстати, сбор и анализ подобных трасс сообщений полезен не только с точки зрения отладки приложения, но и в целях изучения взаимодействия приложения с операционной средой. Это часто позволяет обнаружить и исключить код, вызывающий избыточный поток сообщений, определить правильную последовательность передачи сообщений, выявить наиболее подходящее для обработки сообщение и т.д. Достаточно сказать, что необходимость переноса процедуры сохранения параметров приложения из обработчика сообщения WM_CLOSE в обработчик сообщения WM_SAVEAPPLICATION и возможность организации выпадающих меню в программе БГ(Болван диалоГовый) были выявлены именно в результате анализа трасс сообщений приложения.
Полный текст программы hello находится в архиве hello.zip. Текст программы БГ (Болван диалоГовый) можно найти в статье " Как использовать диалог в качестве главного окна приложения". Я думаю, что Вам не составит особого труда самим дополнить его кодом, необходимым для трассировки (если потребуется :-)). Напомню еще раз, что компилироваться и линковаться такие программы должны как VIO-приложения (а не как PM)!.
Успехов, VicTor Smirnoff
Теперь на счет программ трассировщиков. Ни одна из них, разумеется не сможет показать что происходит после обработки полученного сообщения тем или иным окном, но зато есть программы, которые позволяют перехватить сообщения идущие к этим окнам, посмотреть параметры этих сообщений (в PM API есть соответствующие хуки) или послать тэстовое сообщение. Вот две из них, написанные для OS/2 2.0 в 1992м-1993м годах сотрудниками IBM и до сих пор вполне работоспособные (я их гоняю в WSEB):
При использовании встроенной трассировки, может оказаться полезной библиотека Presentation Manager Monitor 1.0. Она предоставляет API для трассировки с примерами использования. Очень неплохо расшифровывает параметры сообщений.
И еще не совсем по теме, но... софт с IBM Developer Connection online:
Eugen Kuleshov
Интересные ссылки:
Лучших бизнес.
Комментариев к странице: 0 | Добавить комментарий
Редактор: Дмитрий Бан
Оформление: Евгений Кулешов