в данном окне первого значения
Рис. 17.4 Диалоговое окно Add Window с разрешенной поддерж-
кой ObjectWindows
Примечание: Ввод в данном окне первого значения уста-
навливает также класс сообщений в значение "Log all
messages" ("Регистрация всех сообщений").
Вы можете ввести либо имя подпрограммы, которая обрабатывает
сообщения для окна (выберите переключатель Window Object) или
значение-описатель (выберите переключатель Handle). Введите
столько имен подпрограмм или значений описателей, сколько нужно
для отслеживания сообщений ваших окон.
Отмена выбора окна
-----------------------------------------------------------------
Для отмены выбора окна переместите курсор на элемент, затем
либо выберите локальное меню и используйте команду Remove (Уда-
лить), либо нажмите клавиши Del, Ctrl-Y или Ctrl-R.
Чтобы отменить все окна, выберите в локальном меню команду
Delete All (Удалить все).
TDeb 3.0 #3-3 = 22 =
Задание класса сообщений и действия
-----------------------------------------------------------------
Верхняя правая область - это область класса сообщений. Ее
локальное меню идентично локальному меню области выбора окна и
позволяет вам добавить класс сообщения, удалить класс сообщения
или удалить все классы, которые вы добавили.
--------------¬
¦ Add... ¦ Добавить
¦ Remove ¦ Отменить
¦ Delete All ¦ Удалить все
L--------------
Перед тем, как добавить в данной области класс сообщений, в
области выбора окна нужно задать процедуру окна или описатель.
Если вы не задаете конкретный класс сообщений или класс
просматриваемых сообщений, то TDW наблюдает
просматриваемых сообщений, то TDW наблюдает за всеми сообщениями,
передаваемыми процедуре окна или его описателю.
Добавление класса сообщений
-----------------------------------------------------------------
Чтобы добавить класс сообщений, выберите в области класса
сообщений команду локального меню Add (Добавление). TDW выводит
следующее диалоговое окно:
г[*]===Set message filter=================¬
¦ Message Class OK ¦
¦ ( ) All messages ------- ¦
¦ ( ) Mouse ¦
¦ ( ) Window Cancel ¦
¦ ( ) Input ------- ¦
¦ ( ) System ¦
¦ ( ) Initialization Help ¦
¦ ( ) Clipboard ------- ¦
¦ ( ) DDE ¦
¦ ( ) Non-client ¦
¦ ( ) Other ¦
¦ (.) Single message ( ) Break ¦
¦ (.) Log ¦
¦ Single Message name ¦
¦ ---------------------- ¦
L=========================================-
Рис. 17.5 Диалоговое окно Set Message Filter
Message Class - класс сообщений; All messages - все сообще-
ния; Mouse - "мышь"; Window - окно; Input - ввод; System - систе-
ма; Initialization - инициализация; Clipboard - буфер вырезанного
изображения; DDE - динамический обмен данными ; Non-client - не
пользовательское; Other - прочее; Single message - отдельное со-
TDeb 3.0 #3-3 = 23 =
Диалоговое окно Set Message Filter
общение; Break - прерывание; Log - регистрация; Single message
name - имя отдельного сообщения.
Диалоговое окно Set Message Filter (Задание фильтра сообще-
ния) выводит вам подсказку как для класса, так и для действия,
выполняемого при приеме сообщения.
Отладчик TDW по умолчанию регистрирует все сообщения, начи-
нающиеся с WM_. Поскольку поступает так много сообщений, вы, ве-
роятно, захотите ограничить их, выбрав в списке классов сообщений
один из классов. Вы можете добавлять только по одному классу со-
общений. Поэтому, если вы хотите отслеживать сообщения нескольких
классов, для каждого класса сообщений нужно использовать параметр
Add.
TDeb 3.0 #3-3 = 24 =
Классы сообщений описываются в следующей таблице:
Классы сообщений Windows Таблица 17.1
-----------------------T--------------------------------------¬
¦ Класс сообщения ¦ Описание ¦
+----------------------+--------------------------------------+
¦ All messages ¦ Все сообщения, начинающиеся с WM_. ¦
¦ (все сообщения) ¦ ¦
¦ ¦ ¦
¦ Mouse ¦ Сообщения, генерируемые событием от ¦
¦ ("мышь") ¦ "мыши" (например, WM_LBUTTONWODN и ¦
¦ ¦ WM_MOUSEMOVE) ¦
¦ ¦ ¦
¦ Window ¦ Сообщения от менеджера Windows ¦
¦ (окно) ¦ (например, WM_PAINT и WM_CREATE) ¦
¦ ¦ ¦
¦ Input ¦ Сообщения, генерируемые по событию ¦
¦ (ввод) ¦ от клавиатуры или при доступе ¦
создании диалогового блока или окна
¦ ¦ пользователя к меню System, полосе ¦
¦ ¦ прокрутки, или блоку размера ¦
¦ ¦ (например, WM_KEYDOWN и ¦
¦ ¦ WM_SYSCOMMAND) ¦
¦ ¦ ¦
¦ System ¦ Сообщения, генерируемые при ¦
¦ (система) ¦ изменениях в системе (например, ¦
¦ ¦ WM_FONTCHANGE и WM_SPOOLERSTATUS) ¦
¦ ¦ ¦
¦ Initialization ¦ Сообщения, генерируемые при ¦
¦ (инициализация) ¦ создании диалогового блока или окна ¦
¦ ¦ (например, WM_INITDIALOG и ¦
¦ ¦ WM_INITMENU) ¦
¦ ¦ ¦
¦ Clipboard ¦ Сообщения, генерируемые, когда одна ¦
¦ (буфер вырезанного ¦ прикладная задача пытается получить ¦
¦ изображения) ¦ доступ к буферу вырезанного ¦
¦ ¦ изображения или окну другой ¦
¦ ¦ прикладной задачи (например, ¦
¦ ¦ WM_SIZECLIPBOARD и WM_DRAWCLIPBOARD)¦
¦ ¦ ¦
¦ DDE ¦ Сообщения динамического обмена ¦
¦ (динамический ¦ данными, генерируемые прикладными ¦
¦ обмен данными) ¦ программами при коммуникации с ¦
¦ ¦ окнами другой прикладной программы ¦
¦ ¦ (например, WM_DDE_INITIATE и ¦
¦ ¦ WM_DDE_ACK) ¦
¦ ¦ ¦
и введите имя или номер
¦ Non_client ¦ Сообщения, генерируемые Windows ¦
¦ (не пользователь) ¦ для обслуживания непользовательской ¦
¦ ¦ области окна прикладной задачи ¦
¦ ¦ (например, WM_NCHITTEST и ¦
¦ ¦ WM_NCCREATE) ¦
¦ ¦ ¦
TDeb 3.0 #3-3 = 25 =
¦ Other ¦ Любые сообщения, начинающиеся с ¦
¦ (прочие) ¦ WM_, которые не попадают в другие ¦
¦ ¦ категории, например, другие ¦
¦ ¦ сообщения управления отображением ¦
¦ ¦ и многие интерфейсные ¦
¦ ¦ документирующие сообщения ¦
¦ ¦ ¦
¦ Single Message ¦ Любое отдельное сообщение, ¦
¦ (отдельное сообщение)¦ начинающееся с WM_, которое вы ¦
¦ ¦ хотите зарегистрировать или ¦
¦ ¦ прервать программу по сообщению ¦
L----------------------+---------------------------------------
Для отслеживания отдельного сообщения выберите Single
Message (Отдельное сообщение) и введите имя или номер сообщения.
Если вы вводите имя сообщения, учтите, что все буквы должны быть
заглавными.
Действие по умолчанию - помещение сообщения в журнал. Другое
возможное действие, прерывание программы по данному сообщению,
эквивалентно установки для данного сообщения точки останова.
Например, если вы хотите отслеживать сообщение WM_PAINT и
останавливать программу всякий раз, как в окно посылается это со-
общение, вы выбираете область Window Selection (Выбор окна) и вы-
Выверите правую верхнюю область, Message
полняете следующие действия:
1. Выверите правую верхнюю область, Message Class (Класс со-
общений).
2. Вызовите ее локальное меню и выберите команду Add (Добав-
ление).
2. В окне диалога выберите при помощи селективных переключа-
телей Action (Действие) установку Break (Прерывание) и
при помощи селективных переключателей Message Name (Имя
сообщение) установку Single Message (Отдельное сообще-
ние).
4. В блоке ввода Message Name (Имя сообщения) введите
WM_PAINT и нажмите клавишу Return.
На Рис. 17.1 показано, как выглядит окно Windows Messages
после того, как вы сделали данный выбор, и поступило сообщение.
Удаление класса сообщений
-----------------------------------------------------------------
Для удаления класса сообщений поместите курсор на удаляемый
элемент и либо вызовите локальное меню и выберите из него команду
Remove, либо нажмите Delete или клавиши Ctrl-Y.
Для удаления всех классов выберите команду Delete All (Уда-
TDeb 3.0 #3-3 = 26 =
лить все) из локального меню.
После того, как вы удалите все классы, будет установлено
умолчание "Log all messages" ("Регистрация всех сообщений"). Вы
не можете удалить этот класс командами Remove или Delete All.
Просмотр сообщений
-----------------------------------------------------------------
-------------------------¬
¦ Send to log window No¦ Передача в окно регистрации
¦ Erase log ¦ Стереть регистрацию
L-------------------------
Оконные сообщения выводятся в нижней области окна Windows
Messages. Эта область может содержать до 200 сообщений.
Если вы хотите сохранить сообщения
Если вы хотите сохранить сообщения в файле, вы должны отк-
рыть файл журнала для окна Log (выбрав команду View¦Log File (Об-
зор¦Файл регистрации), а затем команду Open Log File (Открытие
файла регистрации) из локального меню). Затем вы должны перейти
обратно в область Messages (Сообщения) и изменить установку Send
To Log Window (Передача в окно регистрации) локального меню в
значение Yes (это означает, что запись в протокол разрешена).
Если вы хотите очистить область от всех сообщений, выберите
команду Erase Log (Стереть протокол) из локального меню. На сооб-
щения, записываемые в окно Log (Регистрация), эта команда не вли-
яет.
Замечания по сообщениям окна
-----------------------------------------------------------------
Если вы выводите сообщения более чем для одного окна, то не
регистрируйте все сообщения. Регистрируйте вместо этого для
каждого окна конкретные сообщения или конкретный класс сообщений,
поскольку большое число сообщений, передаваемых между Windows и
TDW может привести к "зависанию" системы.
При установки прерывания по классу сообщений от "мыши" нужно
учитывать, что перед тем, как снова станет активной клавиатура,
за сообщением mouse down (перемещение "мыши" вниз) должно следо-
вать сообщение mouse up (перемещение "мыши" вверх). Это ограниче-
ние означает, что когда вы возвращаетесь в прикладную программу,
вам может потребоваться несколько раз нажать кнопку "мыши", чтобы
получить сообщение mouse up. Вы узнаете, что в Windows принято
сообщение, когда увидите его в нижней области окна Windows
Messages (Сообщения Windows).
Если вы вводите имя описателя, но указываете, что это проце-
дура, TDW будет воспринимать ваш ввод.
TDW не регистрирует сообщения после
Однако, когда вы запустите
программу, TDW не будет регистрировать никаких сообщений. Если
TDW не регистрирует сообщения после того, как вы установили опи-
TDeb 3.0 #3-3 = 27 =
сатель, чтобы увидеть, выбрали ли вы "кнопку" Handle (Описатель),
проверьте диалоговое окно Set Message Filter (Задание фильтра со-
общений).
TDeb 3.0 #3-3 = 28 =
Получение содержимого памяти и списка модулей
-----------------------------------------------------------------
Для получения содержимого глобальной или локальной динами-
чески распределяемой области или списка модулей вашей программы
для Windows сначала вызовите окно Log командой View¦Log (Просмотр
¦Протокол:), а затем вызовите локальное меню. Последней командой
локального меню окна Log (Регистрация) является команда Display
Windows info (Вывод информации Windows). Выбор этой команды вызо-
вет на экран окно диалога Windows Information, из которого вы мо-
жете выбрать тип списка, который вы хотите вывести на экран, и с
какого места начать вывод.
г[*]=====Windows information========¬
¦ Display ¦
¦ (*) Global heap OK---- ¦
¦ ( ) Local heap ¦
¦ ( ) Module list Cancel ¦
¦ ------ ¦
¦ Started at ¦
¦ (*) Top Help ¦
¦ ( ) Bottom ------ ¦
¦ ( ) Handle ¦
¦ ¦
¦ Starting handle ¦
¦ <not avaliable> ¦
¦ ¦
Если вы выбрали параметр вывода
L===================================-
Рис. 17.6 Диалоговое окно Windows information
Windows information - информация Windows; Display - вывод:
Global heap - глобальная динамически распределяемая область памя-
ти; Local heap - локальная динамически распределяемая область па-
мяти; Module list - список модулей; Started at - начало на...;
Top - вершина; Bottom - конец; Handle - описатель; Starting
handle - начальный описатель; not avaliable - недоступно; Cancel
- отмена; Help - справка.
Если вы выбрали параметр вывода содержимого глобальной дина-
мически распределяемой области памяти, то вы можете выбрать вывод
с верхнего адреса динамически распределяемой области до нижнего,
с нижнего до верхнего, либо с позиции, определяемой начальным ло-
гическим номером (описателем).
Начальный логический номер - это имя логического номера гло-
бальной памяти, устанавливаемого в вашей прикладной программе при
вызове подпрограммы выделения памяти Windows, например
GlobalAlloc. Выбор начального логического номера заставляет TDW
вывести на экран объект, соответствующий этому логическому номеру
(описателю), а также следующие за ним четыре объекта в динамичес-
ки распределяемой памяти.
TDeb 3.0 #3-3 = 29 =
Получение списка содержимого
глобальной динамически распределяемой памяти
-----------------------------------------------------------------
Глобальная динамически распределяемая память - это область
глобальной памяти, которую Windows делает доступной для всех
прикладных программ. Если вы выделяете такие ресурсы, как пиктог-
раммы, элементы изображения, диалоговые окна или шрифты, либо ес-
ли вы выделяете память при помощи функции GlobalAllc, ваша прик-
ладная программа будет использовать глобальную
ладная программа будет использовать глобальную динамически расп-
ределяемую область памяти.
Для получения списка объектов данных в глобальной динамичес-
ки распределяемой области выберите селективный переключатель
Global Heap (Глобальная динамически распределяемая область памя-
ти) в окне диалога Windows Information, а затем выберите "кноп-
ку" OK. Объекты данных будут перечислены в окне Log (Регистра-
ция). Поскольку этот список скорее всего превысит число строк
окна Log (по умолчанию это число равно 50), вы должны либо запи-
сать содержимое в файл журнала (при помощи локального меню окна
Log) или увеличить число строк, которое может использовать окно
Log (для этого служит утилита TDINST). Можно установить максимум
до 200 строк.
Следующая таблица показывает пример с двумя строками вывода
для глобальной динамически распределяемой области памяти и дает
объяснения по каждому их полю:
Формат вывода глобальной динамически распределяемой области
Таблица 17.2
----------------------------------------------------------------¬
¦0EC5 00000040b PDB (OF1D) ¦
¦053 (053D)00002DC0b GDI DATA MOVEABLE LOCKED=0001 PGLOCKED=0001¦
+--------------T------------------------------------------------+
¦Поле ¦ Описание ¦
+--------------+------------------------------------------------+
¦OEC5 ¦Описатель или объект памяти, выраженный ¦
¦ ¦в виде шестнадцатиричного значения из 4 цифр или¦
¦ ¦слова FREE, что указывает на свободный блок ¦
¦ ¦памяти. ¦
¦ ¦ ¦
Селектор памяти, указывающий на запись
¦(053D) ¦ Селектор памяти, указывающий на запись в таблице¦
¦ ¦глобальных дескрипторов. Если у него то же ¦
¦ ¦значение, что и у описателя памяти, то селектор ¦
¦ ¦не выводится. ¦
¦ ¦ ¦
¦00000040b ¦Шестнадцатиричное число, представляющее длину ¦
¦00002DC0b ¦сегмента в байтах. ¦
¦ ¦ ¦
¦PDB ¦Для кого распределяется сегмент. Обычно это ¦
¦GDI ¦прикладная программа или библиотечный модуль. ¦
¦ ¦PDB - это блок описания процесса, который ¦
¦ ¦называют также префиксом программного сегмента ¦
TDeb 3.0 #3-3 = 30 =
¦ ¦PSP. ¦
¦ ¦ ¦
¦(0F1D) ¦Описатель, указывающий на владельца PDB. ¦
¦ ¦ ¦
¦DATA ¦Тип объекта памяти. Тип может быть следующим: ¦
¦ ¦ DATA - сегмент данных прикладной программы или ¦
¦ ¦ DLL ¦
¦ ¦ CODE - сегмент кода прикладной программы или ¦
¦ ¦ DLL ¦
¦ ¦ PRIV - системный объект или глобальные данные ¦
¦ ¦ для прикладной программы или DLL. ¦
¦ ¦ ¦
¦MOVEABLE ¦Атрибут распределения памяти. Объект может ¦
¦ ¦иметь атрибуты FIXED, MOVEABLE или MOVEABLE ¦
¦ ¦DISCARDABLE. ¦
Для объекта MOVEABLE или MOVEABLE
¦ ¦ ¦
¦LOCKED=00001 ¦ Для объекта MOVEABLE или MOVEABLE DISCARDABLE ¦
¦ ¦это число блокировок объекта, который ¦
¦ ¦используется с помощью функции GlobalLock или ¦
¦ ¦LockData. ¦
¦ ¦ ¦
¦FGLOCKED=00001¦Для улучшенного режима 386 число блокировок ¦
¦ ¦страниц для объекта, который использует ¦
¦ ¦функцию GlobalPageLock. Если страничная ¦
¦ ¦блокировка установлена на объект памяти, то ¦
¦ ¦Windows не может выгрузить на диск ни одну ¦
¦ ¦из 4-килобайтовых страниц объекта. ¦
L--------------+-------------------------------------------------
Листинг содержимого локальной динамически распределяемой области
-----------------------------------------------------------------
Локальная динамически распределяемая область памяти - это
частная область памяти для прикладной программы. Для других прик-
ладных программ Windows (включая другие экземпляры той же прог-
раммы) она недоступна.
Программа не обязательно имеет локальную динамически распре-
деляемую область памяти. Windows создает локальную динамически
распределяемую область памяти прикладной программы с помощью
функции LocalAlloc.
Чтобы просмотреть список объектов данных в локальной динами-
чески распределяемой области памяти, выберите селективный перек-
лючатель Local Heap (Локальная динамически распределяемая область
памяти) в диалоговом окне Windows Information (Информация
Windows), затем выберите "кнопку" OK. В окне Log (Протокол) выве-
дется список объектов данных.
Поскольку размер списка будет вероятно
TDeb 3.0 #3-3 = 31 =
Поскольку размер списка будет вероятно превышать число строк
в окне Log (по умолчанию 50), вам следует записать содержимое в
файл регистрации (с помощью локального меню окна Log) или увели-
чить число строк в окне Log (с помощью программы TDWINST). Можно
задать до 200 строк.
В следующей таблице приведена типичная строка вывода локаль-
ной динамически распределяемой области с последующим пояснением
ее формата.
TDeb 3.0 #3-3 = 32 =
Формат вывода локальной динамически распределяемой области
Таблица 17.3
----------------------------------------------------------------¬
¦ 05CD: 0024 BUSY (10AF) ¦
+-------------T-------------------------------------------------+
¦ Поле ¦ Описание ¦
+-------------+-------------------------------------------------+
¦ OEC5 ¦ Смещение объекта в локальном сегменте данных. ¦
¦ ¦ ¦
¦ 0024 ¦ Длина объекта в байтах. ¦
¦ ¦ ¦
¦ BUSY ¦ Размещение объекта памяти: ¦
¦ ¦ FREE - нераспределенный блок памяти; ¦
¦ ¦ BUSY - распределенный объект. ¦
¦ ¦ ¦
¦ (10AF) ¦ Описатель локальной памяти для объекта. ¦
L-------------+--------------------------------------------------
Получение списка модулей
-----------------------------------------------------------------
Чтобы увидеть список модулей задачи или DLL, которые загру-
в диалоговом окне Windows Information
жены Windows, выберите селективный переключатель Module List
(Список модулей) в диалоговом окне Windows Information (Информа-
ция Windows), затем выберите "кнопку" OK. Список модулей выведет-
ся в окне Log (Протокол).
В следующей таблице показан пример - три выводимые строки
списка модулей с последующим пояснением по каждому полю.
TDeb 3.0 #3-3 = 33 =
Формат вывода списка модулей Windows (Таблица 17.7)
----------------------------------------------------------------¬
¦Пример вывода списка модулей ¦
+---------------------------------------------------------------+
¦0985 TASK TDW C:\TD\TDW.EXE ¦
¦0E2D DLL WINDEBUG C:\WIN3\WINDEBUG.DLL ¦
¦0EFD TASK GENERIC C:\TD\GENERIC.EXE ¦
+------------------T--------------------------------------------+
¦Поле ¦ Описание ¦
+------------------+--------------------------------------------+
¦0EFD ¦Логический номер сегмента памяти, выраженный¦
¦ ¦4-значным шестнадцатиричным значением. ¦
¦ ¦ ¦
¦TASK ¦Тип модуля. Модуль может быть либо задачей, ¦
¦ ¦либо DLL. ¦
¦ ¦ ¦
¦GENERIC ¦Имя модуля. ¦
¦ ¦ ¦
¦C:\TD\GENERIC.EXE ¦Маршрут к выполняемому файлу модуля. ¦
L------------------+---------------------------------------------
Отладка динамически компонуемых библиотек (DLL)
-----------------------------------------------------------------
DLL представляет собой библиотеку подпрограмм
DLL представляет собой библиотеку подпрограмм и ресурсов
Windows, компонуемых с вашей прикладной программой во время вы-
полнения, а не компиляции. Такая компоновка во время выполнения
позволяет нескольким прикладным программам разделять одну копию
подпрограмм, данных или драйверов устройств, экономя тем самым
память. При запуске прикладной программы, использующей DLL, в
случае, если DLL еще не загружена в память, Windows загружает ее
таким образом, чтобы программе стали доступны ее точки входа.
При загрузке в TDW прикладной программы, с которой компону-
ются DLL, TDW определяет, какие из этих библиотек DLL (если они
используются) имеют таблицы имен идентификаторов (были скомпили-
рованы с параметром включения отладочной информации) и трассирует
для вас эти библиотеки. Если во время выполнения прикладной прог-
раммы TDW встречает вызов точки входа одной из этих DLL, то TDW
загружает таблицу имен идентификаторов и исходный код этой библи-
отеки и устанавливает маркер строки модуля в начало подпрограммы
DLL, вызываемой прикладной программой. Затем DLL становится в ок-
не Module, так же как и коды вашей прикладной программы.
Примечание: Отладчик TDW может загрузить DLL, не имею-
щую таблицы имен идентификаторов, но только в окно CPU и
только при отладке именно в окне CPU.
При выходе из подпрограммы DLL TDW перезагружает таблицу
имен идентификаторов и исходный код вашей программы и устанавли-
вает маркер строки на следующий оператор после вызова точки входа
DLL.
TDeb 3.0 #3-3 = 34 =
Если вы выполняете трассировку программы с помощью клавиш F7
и F8, для TDW может оказаться невозможным возврат в вызывающую
программы из подпрограммы, поскольку DLL может возвращать управ-
как если бы вы нажали
ление через вызов функции Windows. В этом случае ваша программа
просто выполняется так, как если бы вы нажали клавишу F9. Такое
проведение является общим для кода начальной загрузки библиотеки
DLL. Чтобы принудительно вернуться в прикладную программу перед
началом трассировки прикладной программы до вызова DLL установите
в вашей прикладной программе точку останова на строке после обра-
щения к DLL. При отладке кода начальной загрузки DLL (кода иници-
ализации) установите точку останова на первой строке прикладной
программы.
Поскольку отладка DLL в TDW значительно автоматизирована,
вам не нужно задавать, какую DLL нужно загрузить. Однако, вам мо-
жет понадобиться выполнить и другие задачи, например:
- Добавить DLL в список DLL;
- Установить точки останова, прерывания, слежения и т.д. в
DLL;
- Задать, какие DLL TDW не должен загружать;
- Отладить начальный код загрузки DLL.
Для выполнения любой из этих задач вы должны войти в окно
диалога Load Modules or DLLs ("Загрузка модулей или DLL") при по-
мощи команды View¦Modules (Обзор¦Модули). (Это диалоговое окно
также выводится при нажатии клавиши F3).
TDeb 3.0 #3-3 = 35 =
г[*]==========Load modules or DDL symbols==================¬
¦ ¦
¦ Source modules DDLs & Programs ¦
¦ -demo----------- Load -SCRNFUNC.DDL*-- Symbol load ¦
¦ ---------------- -DEMO.EXE*------ ----------- ¦
¦ ---------------- Cancel -WINDEBUG.DDL--- Load symbols ¦
¦ ---------------- -WREMOTE.EXE---- ( ) No ¦
¦ ---------------- Help -NWPOPUP.EXE---- (*) Yes ¦
¦ ---------------- ----- -PROGRAM.EXE---- ¦
Диалоговое окно Load modules
¦ ---------------- -USER.EXE------- Debug startup¦
¦ ---------------- -GDI.EXE-------- ( ) No ¦
¦ ---------------- -KRNL386.EXE---- (*) Yes ¦
¦ ---------------- ---------------- ¦
¦ ---------------- ¦
¦ ---------------- DDL name ¦
¦ ---------------- ---------------- Add DDL ¦
¦ ---------------- ---------------- ----------- ¦
¦ ¦
L==========================================================-
Рис. 17. 7 Диалоговое окно Load modules or DDLs
Load modules or DDL symbols - загрузка модулей или идентифи-
каторов DDL; Source modules - исходные модули; DDLs & Programs -
библиотеки DDL и программы; Load - загрузка; Symbol load - заг-
рузка идентификаторов; Cancel - отмена; Help - справка; Debug
startup - отладка кода инициализации; Add DDL - добавить DDL; DDL
name - имя библиотеки DDL.
Использование диалогового окна Load Modules or DLLs
------------------------------------------------------------------
Это диалоговое окно позволяет:
- Перейти к другому исходному модулю вашей прикладной прог-
раммы;
- Выполнить операции (такие, как загрузка файла имен иденти-
фикаторов и исходного файла) с DLL и файлами .EXE.
TDeb 3.0 #3-3 = 36 =
Переход к другому исходному модулю
-----------------------------------------------------------------
Если вы отлаживаете прикладную программу, состоящую из нес-
кольких исходных модулей, скомпонованных в один файл .EXE, и вам
нужен доступ к модулю программы, отличному от текущего загружен-
вы можете вызвать окно Load
ного в окно Module, вы можете вызвать окно Load Modules or DLLs
(Загрузка модулей или DDL) и выбрать один из модулей в левом
списке, который называется Source Modules (Исходные модули). В
этом списке перечислены все модули, скомпонованные в вашей прик-
ладной программе.
Для того, чтобы выбрать модуль, выделите его, нажмите клави-
шу Enter или дважды нажмите кнопку "мыши", либо выберите окно
Load (Загрузка). Отладчик TDW выведет на дисплей окно Module (Мо-
дуль), и в нем будет находиться новый исходный модуль.
Работа с DLL и программами
-----------------------------------------------------------------
При отладке прикладной программы, с которой связана одна или
более DLL (это верно для любых прикладных программ для Windows),
и вы вызвали окно диалога Load Modules or DLLs (Загрузка модулей
или DDL), вы увидите там список DLLs & Programs (DLL и програм-
мы), в котором перечислены DLL и файлы .EXE. Этот список включает
в себя все DLL и файлы .EXE, загруженные Windows в текущий мо-
мент, а также все библиотеки DLL, запускаемые при запуске прик-
ладной программы. Он не включает DLL, которые ваша прикладная
программа запускает вызовом LOADLIBRARY, если только они уже не
загружены программой или Windows.
Элементы в верхней части списка, помеченные справа многото-
чием, это файлы .EXE вашей прикладной программы и DLL с таблицами
имен идентификаторов, вызываемые вашей прикладной программой. Ес-
ли вы не не делали изменений, отладчик TDW автоматически загружа-
ет таблицу имен идентификаторов и исходный код для каждой поме-
ченной DLL, всякий раз при вызове этой DLL из вашей программы.
Кроме того, TDW автоматически загружает таблицу имен идентифика-
торов и исходный код любой библиотеки DLL, которую ваша программа
даже хотя сначала эта DLL
запускает вызовом LOADLIBRARY, даже хотя сначала эта DLL может и
не присутствовать в списке. (Она там появится после загрузки ее
TDW).
"Кнопки" справа от списка позволяют выполнять операции с вы-
деленными вами DLL или прикладными программами. Окно текстового
ввода под списком позволяет добавить в список DLL. Вы можете ис-
пользовать эти средства следующим образом:
TDeb 3.0 #3-3 = 37 =
Управляющие "кнопки"окна диалога DLLs & Programs Таблица 17.5
--------------------T-------------------------------------------¬
¦ Кнопка ¦ Описание ¦
+-------------------+-------------------------------------------+
¦ Symbol load ¦ Загружает таблицу имен идентификаторов и ¦
¦ ¦ исходные файлы для DLL или прикладной ¦
¦ ¦ программы. Эта команда изменяет содержимое¦
¦ ¦ окна Module (Модуль), и вы можете ¦
¦ ¦ устанавливать точки останова, сообщения ¦
¦ ¦ Windows, и т.д. для DLL. ¦
¦ ¦ ¦
¦ Load Symbols ¦ Задает, загружать ли таблицу имен ¦
¦ (No/Yes) ¦ идентификаторов DLL и ее исходный код, ¦
¦ ¦ когда прикладная программа вызывает DLL. ¦
¦ ¦ Вы можете использовать этот параметр, ¦
¦ ¦ чтобы предотвратить загрузку данной ¦
¦ ¦ информации для библиотеки, которая не ¦
¦ ¦ требует отладки. По умолчанию будет ¦
¦ ¦ установлено значение Yes (Да). ¦
¦ ¦ ¦
¦ Debug startup ¦ Задает, нужно ли отлаживать код начальной ¦
использовать для этой DLL одну
¦ (No/Yes) ¦ загрузки DLL. По умолчанию устанавливается¦
¦ ¦ No (Нет). ¦
¦ ¦ ¦
¦ DLL Name ¦ Введите имя DLL, отсутствующей в списке ¦
¦ ¦ DLLs & Programs (DDL и программы), чтобы ¦
¦ ¦ добавить библиотеку в этот список. ¦
¦ ¦ Добавление DLL в список позволит затем ¦
¦ ¦ использовать для этой DLL одну из трех ¦
¦ ¦ предыдущих команд. При необходимости можно¦
¦ ¦ задать полный маршрут. ¦
¦ ¦ ¦
¦ Add DLL ¦ Добавить DLL в текущий блок текстового ¦
¦ ¦ ввода в список DLLs & Programs. ¦
L-------------------+--------------------------------------------
Добавление DLL в список DLLs & Programs
-----------------------------------------------------------------
Прежде чем вы сможете установить отладочные параметры, необ-
ходимость отлаживать начальный код загрузки DLL или загружать
таблицу имен идентификаторов и исходный код DLL, эта DLL должна
быть внесена в список DLL & Programs (DLL и программы). Библиоте-
ка DLL, к которой обращается ваша программа, может и не быть в
этом списке, поскольку сразу после загрузки программы TDW знает
только о DLL, компонуемых с начальным кодом загрузки прикладной
программы. Программа может также запустить DLL явно, при помощи
команды LOADLIBRARY. TDW не знает об этом до вызова LOADLIBRARY.
TDeb 3.0 #3-3 = 38 =
Существует два различных типа кода начальной загрузки, упо-
минаемые в данном разделе: начальный код загрузки вашей приклад-
и начальный код загрузки DLL.
ной программы и начальный код загрузки DLL. Некоторые динамически
компонуемые библиотеки DLL запускаются начальным кодом загрузки
вашей прикладной программы. Когда ваша программа запускает DLL,
выполняется начальный код загрузки DLL.
Если вы хотите добавить DLL в список DLLs & Programs, вызо-
вите диалоговое окно Load Modules or DLLs (Загрузка модулей или
DDL) (нажмите клавишу F3 или используйте команду View¦Modules
(Обзор¦Модули)), перейдите к блоку текстового ввода DLL Name (имя
DLL), введите имя DLL (если необходимо, введите полный маршрут),
и активизируйте "кнопку" Add DLL, чтобы добавить это имя в спи-
сок.
TDeb 3.0 #3-3 = 39 =
Установка параметров отладки для DLL
-----------------------------------------------------------------
Если вы хотите установить для DLL точки останова или слеже-
ния, или еще какие-либо параметры отладки, вызовите окно диалога
Load Modules or DLLs (Загрузка модулей или DDL) (нажмите клавишу
F3 или выберите команду View¦Modules), найдите DLL в списке DLLs
& Programs, выделите его и затем используйте команду Symbol Load
(Загрузка идентификатора), чтобы получить окно Module с этой биб-
лиотекой DLL в нем. В окне Module (Модуль) вы можете выполнить с
DLL интересующие вас операции.
Управление загрузкой TDW таблиц имен идентификаторов DLL
-----------------------------------------------------------------
По умолчанию TDW загружает таблицу имен идентификаторов и
исходный код для каждой DLL, к которой обращается ваша прикладная
программа, но только если DLL имеет совместимую таблицу имен
идентификаторов. DLL имеет таблицу имен идентификаторов, совмес-
тимую с TDW. При компиляции с включенной информации для отладки и
с помощью компилятора Турбо Паскаля для Windows DLL содержит таб-
Поскольку загрузка отладочной информации для
лицу идентификаторов, совместимую с TDW.
Поскольку загрузка отладочной информации для DLL и затем
загрузка отладочной информации для прикладной программы после то-
го, как DLL отработает, занимает время, вам может понадобиться
отменить стандартные действия для DLL, которую вы не хотите отла-
живать. Чтобы TDW не загружал таблицу имен идентификаторов для
DLL, вызовите окно диалога Load Modules or DLLs (нажатием клавиши
F3 или командой View¦Modules), найдите имя этой DLL в списке DLLs
& Programs, выделите его и активизируйте "кнопку" Load Symbols
(Загрузка идентификаторов) со значением No (Нет).
Отладка начального кода загрузки DLL
-----------------------------------------------------------------
По умолчанию TDW не отлаживает начальный код загрузки DLL, а
только загружает таблицу имен идентификаторов DLL, когда ваша
прикладная программа вызывает точку входа этой DLL. Затем TDW вы-
зывает окно Module (Модуль) или CPU (ЦП) с маркером текущей стро-
ки на начале подпрограммы DLL, вызванной прикладной программой.
Отладчик TDW будет отлаживать начальный код загрузки DLL,
если вы явно зададите это. Вы можете использовать TDW для отладки
любого из двух типов начального кода загрузки DLL.
Какого рода начальный код загрузки вы отлаживаете
- Код инициализации, непосредственно после LibMain (по умол-
чанию).
- Ассемблерный код, скомпонованный с DLL, выполняющий на-
чальную загрузку и содержит эмулированные математические
TDeb 3.0 #3-3 = 40 =
пакеты для используемой DLL модели памяти (выбираемой при
запуске TDW параметром командной строки -l).
После того, как вы задали отладку начального кода загрузки
одной или более DLL вашей прикладной программы, TDW загружает
таблицу имен идентификаторов для каждой
таблицу имен идентификаторов для каждой библиотеки DLL либо когда
начальный код загрузки вашей прикладной программы запускает эту
DLL, либо когда прикладная программа явно делает вызов
LOADLIBRARY.
Загружена ли уже ваша прикладная программа?
Если вы пытаетесь загрузить прикладную программу, а уже за-
тем задать отладку начального кода загрузки, отладчик TDW может
повести себя иначе, чем вы ожидали, так как некоторые или все
библиотеки DLL могут уже оказаться загруженными. Следовательно,
задавать отладку начального кода нужно:
- Установив отладку для DLL до загрузки вашей прикладной
программы.
- Загрузив вашу прикладную программу, указав DLL для отладки
начального кода загрузки, а затем перезапустив программу
(Ctrl-F2 или Run¦Program Reset).
Выполнение отладки начального кода загрузки
Учитывая все эти предварительные замечания, используйте для
задания отладки начального кода загрузки для одной или более DLL
и для отладки начального кода загрузки этих библиотек следующие
шаги:
1. Вызовите окно диалога Load Modules or DLL (нажмите F3 или
воспользуйтесь командой View¦Modules (Обзор¦Модули).
2. Найдите DLL в списке DLLs & Programs и выделите ее.
3. Активизируйте "кнопку" Debug Startup (Отладка кода иници-
алиазции) значением Yes (Да).
4. Повторите шаги 2 и 3, пока вы не зададите отладку началь-
ного кода загрузки для всех библиотек DLL, для которых
это необходимо.
5. Если нужная вам DLL находится не в списке или в списке
нет DLL (поскольку вы еще не загрузили прикладную прог-
рамму), используйте поле текстового ввода DLL Name для
ввода каждого имени DLL и добавления его в список при по-
После того, как вы установили
мощи "кнопки" Add DLL (Добавление DLL), а затем выберите
"кнопку" Debug Startup Yes (Отладка начального кода раз-
решена).
6. После того, как вы установили все DLL, для которых вы хо-
TDeb 3.0 #3-3 = 41 =
тите отлаживать начальный код загрузки, следующим шагом
будет либо загрузка при помощи команды File¦Load
(Файл¦Загрузка) вашей прикладной программы (если вы еще
ее не загрузили), либо перезагрузка программы при помощи
команды Run¦Program Reset (Выполнение¦Сброс программы)
(если вы загрузили ее до того, как задали отладку началь-
ного кода загрузки).
7. Перед выполнением прикладной программы вам следует уста-
новить точки останова, чтобы после выполнения кода на-
чальной загрузки DLL возвращаться в прикладную программу.
Когда в окне Module находится исходный код прикладной
программы, сделайте следующее:
а) установите на первой строке прикладной программы
точку останова;
б) если вы отлаживаете код начальной загрузки для какой-
либо из DLL, загруженной вызовом LoadLibrary, уста-
новите точку останова на первой строке кода после
каждого из этих вызовов.
7. Когда ваша прикладная программа запускает каждую из DLL,
отладчик TDW выводит либо LibMain DLL в окне Module (по
умолчанию), либо начало листинга ассемблерного кода на-
чальной загрузки библиотеки в окне CPU (так как TDW запу-
щен с параметром -l).
8. Когда вы закончили отлаживать начальный код загрузки DLL,
нажмите клавишу F9 для того, чтобы выполнить до конца на-
чальный код загрузки и вернуться к прикладной программе.
Если вы задали еще другие
Если вы задали еще другие библиотеки DLL для отладки на-
чального кода загрузки, TDW выводит на экран начальный
код для этих библиотек DLL, как только программа запуска-
ет каждую из них.
Не забудьте выполнить до конца начальный код загрузки DLL,
прежде чем перезагрузить текущую прикладную программу или загру-
зить новую. Если вы забудете сделать это, частично выполненный
начальный код DLL может привести к тому, что Windows "зависнет",
и вам придется перезагружаться.
TDeb 3.0 #3-3 = 42 =
Преобразование описателей памяти в адреса
-----------------------------------------------------------------
Windows использует для объектов не адреса памяти, а логичес-
кие номера (описатели) памяти, так как выполняет собственную ор-
ганизацию памяти и может изменить физическое расположение объекта
в памяти. Если вам нужен фактический адрес, соответствующий логи-
ческому номеру, вы можете использовать для этого встроенные имена
Турбо отладчика - lh2fp (для локальных логических номеров) и
gh2fp (для глобальных логических номеров) для обращения по логи-
ческому номеру к адресу памяти.
Вы можете использовать эти имена для приведения типов в TDW,
как для приведения типов указателей в Турбо Паскале обычные ис-
пользуются символические имена. Например, вы можете преобразовать
локальный логический номер hLocalMemory двумя методами:
- Вы можете использовать окно Data¦Inspect (Данные¦Проверка)
для вычисления следующего выражения:
(lh2fp) hLocalMemory
- Вы можете использовать команду Type Cast (Приведение типа)
окна Inspector (Проверка) и ввести там lh2fp в качестве
типа.
В любом случае выражение вычисляется для первого символа
блока памяти, на который указывает
блока памяти, на который указывает HLocalMemory.
Вы можете также использовать любой из этих методов для вы-
полнения более сложного приведения типов - например, для двухша-
гового приведения от логического номера к дальнему символьному
указателю на дальний указатель данных в памяти, следующим обра-
зом:
(Mystruct far *) (lh2fp)hLocalMemory
Рекомендации по отладке
-----------------------------------------------------------------
Поскольку прикладные программы для Windows являются интерак-
тивными программами, лучший способ отладки состоит в том, чтобы
запустить ее и останавливать в точках останова.
Как первичное средство отладки пошаговое выполнение или
трассировка программы для Windows может оказаться недостаточным,
так как в конце концов вам встретится код, который зациклится в
ожидании сообщения для окна. Для таких случаев вы можете устано-
вить, там где это возможно, точки останова для кода и для сообще-
ний, выполнить программу до такой точки останова, а затем уже пе-
рейти к пошаговому выполнению или трассировке.
При пошаговом режиме в цикле приема сообщения вы можете на-
TDeb 3.0 #3-3 = 43 =
жать комбинацию клавиш Alt-F5, чтобы видеть экран прикладной
программы, но вы не сможете с ней взаимодействовать. Для этого вы
должны нажать клавишу F9 для запуска программы, чтобы видеть окна
программы, однако как быть, если вам нужно попасть обратно в TDW
для трассировки ошибки, которую вы обнаружили, пока использовали
одно из окон вашей программы?
В случае прикладной программы для DOS вы можете нажать Ctrl-
Break, чтобы прервать программу и вернуться к Турбо отладчику,од-
нако в случае Windows эта команда не работает. Однако, и здесь
есть способ прервать программу: нажмите комбинацию клавиш
нить любые другие действия по
Ctrl-Alt-SysRq. Попав обратно в TDW, вы можете установить точки
останова для кода и сообщений, задать выражения просмотра, прос-
мотреть все сообщения, зарегистрированные в протоколе, или выпол-
нить любые другие действия по локализации ошибки. Как только вы
будете готовы вернуться в отлаживаемую программу, нажмите клавишу
F9.
Когда ваша программа прервана, нельзя делать следующее:
- Продолжить отладку пошаговым выполнением. Попытка пошаго-
вого выполнения после того, как произошло прерывание, при-
ведет к непредсказуемым результатам, поскольку ваша прог-
рамма могла выполнять код Windows. Обычно в этом случае
Windows завершает как вашу прикладную программу, так и
TDW, выводя сообщение: "Unrecoverable application error"
("Невосстановимая ошибка прикладной программы").
- Если ваша прикладная программа выполняла код Windows, не
завершайте выполнение ни прикладной программы, ни отладчи-
ка TDW. Если вы это сделаете, то Windows может сбиться и
"зависнуть", и вам придется перезагружать систему. Если в
этой ситуации вы попытаетесь выйти из программы или пере-
загрузить ее, TDW выведет диалоговое окно с запросом:
Ctrl-Alt-SysRq interrupt. System crash possible. Continue ?
(Прерывание по Ctrl-Alt-SysRq. Возможен сбой системы. Про-
должить?)
TDeb 3.0 #3-3 = 44 =
Сообщения об ошибках TDW
-----------------------------------------------------------------
Существует два сообщения об ошибке, возвращаемые исключи-
тельно отладчиком TDW. Кроме того, могут возвращаться и сообщения
об ошибках Турбо отладчика, распространяющиеся и на работу TDW,
например "Symbol not found" в ответ на несуществующее имя оконной
SysRq interrupt. System crash possible.
процедуры.
Ctrl-Alt- SysRq interrupt. System crash possible. Continue ?
(Прерывание по Ctrl-Alt-SysRq. Возможен сбой системы. Про-
должить?)
Вы пытались либо выйти из TDW, либо перезагрузить отлаживае-
мую прикладную программу, когда программа была приостановлена в
результате нажатия клавиш Ctrl-Alt-SysRq. Поскольку в момент при-
остановки программы выполнялся код ядра Windows, выход из TDW или
перезагрузка прикладной программы может привести к непредсказуе-
мым результатам (вероятнее всего, к зависанию системы и необходи-
мости ее перезагрузки).
Если это возможно, установите в коде точку останова и снова
запустите программу. Когда ваша программа встретит эту точку ос-
танова и выйдет в TDW, вы можете завершить работу с TDW или пере-
загрузить вашу программу.
Invalid window handle
(Неверный логический номер окна)
Пытаясь установить регистрацию сообщений для окна вашей
программы, вы ввели имя переменной логического номера (описате-
ля), которой не был присвоен логический номер. Пройдите в пошаго-
вом режиме до оператора, где этот логический номер присваивается
переменной, и попробуйте снова ввести это имя.
TDeb 3.0 #3-3 = 45 =
Глава 18. Отладка прикладной программы для Windows
-----------------------------------------------------------------
В данной главе описывается отладка стандартной прикладной
программы Windows, написанной без библиотечного класса
ObjectWindow.
Как было сказано в Главе 17, отладка прикладной программы
для Microsoft Windows во многом похожа на отладку программы для
DOS, особенно если речь идет об интерактивной программе для DOS.
Все методы отладки, описанные в Главе 14, "Отладка программы",
Поскольку прикладная программа для Windows
применимы также и для программы для Windows.
Поскольку прикладная программа для Windows использует интер-
фейс прикладных программ для Windows (Application Program
Interface - API), существует множество дополнительных способов
работы с программой. Данная глава не претендует на то, чтобы нау-
чить вас способам безошибочного программирования в Windows. Ее
цель состоит в том, чтобы продемонстрировать средства TDW на при-
мере отладки прикладной программы.
Примеры программ
-----------------------------------------------------------------
С отладчиком TDW поставляются три примера программ, и все
они требуют наличия "мыши". Это программы:
- BCWDEMO.EXE, выполняемый файл программы Simple Paint. Эта
программа позволяет строить линии, эллипсы и прямоугольни-
ки тремя цветами и тремя толщинами линии. Она устанавлива-
ется для работы под управлением менеджера программ, и мо-
жет быть вызвана посредством выбора соответствующей
пиктограммы. Вместе с BCWDEMO.EXE также поставляются файлы
исходного текста, BCWDEMO.C, и файл проекта, BCWDEMO.PRJ.
- BCWDEMOA.EXE, выполняемый файл, который содержит все ошиб-
ки. С ним также поставляются файлы исходного текста,
BCWDEMOA.C, и файл проекта, BCWDEMOA.PRJ.
- BCWDEMOB.EXE, выполняемый файл, который содержит все те же
ошибки, что и BCWDEMOA, кроме первой. С ним также постав-
ляются файлы исходного текста, BCWDEMOB.C, и файл проекта,
BCWDEMOB.PRJ.
TDeb 3.0 #3-3 = 46 =
Кроме того, имеется несколько файлов, общих для всех трех
версий программы, а именно:
- BCWDEMO.DEF, это файл определения модуля.
- BCWDEMO.H, это файл заголовка.
это исходный файл для получения
- BCWDEMO.RC, это исходный файл для получения ресурсного
файла.
- BCWDEMO.RES это скомпилированный ресурсный файл.
TDeb 3.0 #3-3 = 47 =
Компиляция и компоновка демонстрационных программ
-----------------------------------------------------------------
Вам предоставляется три версии демонстрационной программы,
которые упрощают изучение данной главы. При желании вы можете са-
ми внести изменения в эти программы. С помощью компилятора
Borland C++ вы можете скомпилировать и скомпоновать их в интерак-
тивной среде разработки разработки программ Borland, используя
имеющиеся файлы проекта.
Более подробную информацию о компиляции программ для Windows
с использованием файлов проекта см. в "Руководстве пользователя
для Borland C++."
Отладка программы BCWDEMOA
-----------------------------------------------------------------
Примечание: Прежде чем начать сеанс отладки, вы можете
немного поэкспериментировать с BCWDEMO.EXE, чтобы понять,
что собственно должна делать эта программа.
Первая отлаживаемая программа называется BCWDEMOA. Запустите
Windows, TDW и запустите BCWDEMOA. Когда в окне Module появится
исходный код программы, нажмите клавишу F9 для запуска примера
программы.
На экране появится курсор в виде "песочных часов", который
означает, что программа работает и выполняет некоторые действия.
Обычно курсор в виде часов скоро исчезает, и на его месте появля-
ется курсор в виде стрелки. Если песочные часы не исчезают, зна-
чит, что-то происходит неверно.
Чтобы перейти в TDW для выяснения причин неверной работы,
нажмите клавиши Ctrl-Alt-SysRq, чтобы приостановить выполнение
программы.
После того, как вы оказались
Принятие решения о дальнейших действиях
-----------------------------------------------------------------
После того, как вы оказались в TDW, следующим шагом вы долж-
ны решить, каким именно образом выяснить, что в программе невер-
но. Обычно если прикладная программа для Windows зависает и не
позволяет судить о ходе ее выполнения, вы перезагружаетесь и на-
чинаете пошаговую отладку подпрограмм инициализации окна. Дойдя
до цикла сообщений, вы можете задать регистрацию сообщений
Windows, можете установить их регистрацию, чтобы затем посмот-
реть, какие сообщения поступают в программу.
Так как исходный экран программы все же был отображен, вы
знаете, что программа дошла до цикла сообщений, поскольку для то-
го, чтобы нарисовать этот экран, она должна была обрабатывать со-
общения Windows. Следовательно, вы можете вместо пошагового вы-
полнения сразу же перейти к регистрации сообщений. Зная, какие
TDeb 3.0 #3-3 = 48 =
сообщения были обработаны, вы как минимум будете знать, какие
участки кода программы были выполнены.
Завершение BCWDEMOA
-----------------------------------------------------------------
В этой точке вы можете задать, какие сообщения вы хотите ре-
гистрировать, и вернуться к программе, нажав клавишу F9, однако
выйдя из программы и снова запустив ее, вы сможете получить более
точную картину происходящего. Поскольку с помощью клавиш Ctrl-Alt
-SysRq вы только приостановили выполнение программы, то выход из
программы в этой точке может привести к зависанию системы (если в
момент приостановки выполнялось ядро Windows). Для завершения
программы выполните следующие шаги:
1. Перезагрузите программу командой Run¦Program Reset
После перезагрузки вы получите окно
(Выполнение¦Сброс программы) (клавиши Ctrl-F2).
2. После перезагрузки вы получите окно модуля с WinMain. Ес-
ли все так и произошло, вы можете пропустить все дальней-
шие шаги и перейти к разделу "Регистрация сообщений".
3. Если же вы получили окно с сообщением "Ctrl-Alt-SysRq
Interrupt. System Crash possible. Continue?" ("Прерывание
по Ctrl-Alt-SysRq. Возможен сбой системы. Продолжить?",
выберите No (Нет), чтобы отменить выход из программы. В
этом случае выход должен выполняться в точке останова по
сообщению.
4. Вызовите команду View¦Windows Messages, чтобы вызвать
окно Windows Message (Сообщения Windows).
5. Курсор находится в верхней левой области, Window
Selection (Выбор окна). Вызовите локальное меню (нажав
Alt-F10) и в нем выберите команду Add (Добавление).
6. Введите имя подпрограммы, в которой обрабатываются сооб-
щения для данного окна. В этой программе имеется только
одно окно, и следовательно, только одна подпрограмма об-
работки сообщений, WndProc.
7. Нажмите клавишу Enter, чтобы подтвердить ввод.
8. При помощи клавиши Tab или "мыши" перейдите в правую об-
ласть, Message Class (Класс сообщений), затем вызовите
локальное меню этой области и выберите там команду Add
(Добавление).
TDeb 3.0 #3-3 = 49 =
9. В окне диалога Set Message Filter (Установка фильтра со-
общений) выберите All Messages (Все сообщения) в качестве
класса сообщений и Break (Прерывание) в качестве дейс-
твия, и нажмите Enter, чтобы добавить этот класс сообще-
ний. TDW затем прекратит выполнение программы на первом
Для возобновления работы программы нажмите
же сообщении.
10. Для возобновления работы программы нажмите F9. На следую-
щем же сообщении программа прервется, и управление возв-
ратится в TDW.
11. В TDW установите курсор на WinMain и при помощи команды
Run¦Program Reset (Выполнение¦Сброс программы) перезагру-
зите программу. Затем вы сможете продолжить отладку.
TDeb 3.0 #3-3 = 50 =
Регистрация сообщений
-----------------------------------------------------------------
На этот раз, прежде чем запустить программу, вы зададите TDW
регистрацию всех сообщений. Откройте окно Windows Messages (Сооб-
щения Windows) при помощи команды View¦Windows messages (Об-
зор¦Сообщения Windows) и добавьте в верхней левой области
WndProc. В правой верхней области должна появиться надпись Log
all messages (Регистрация всех сообщений). Это именно то, что вам
нужно, и работа с этим окном закончена.
Поскольку заранее число регистрируемых сообщений неизвестно,
может случиться так, что до выхода из программы предельное число
регистрируемых сообщений (200) будет превышено, поэтому вы должны
обеспечить запись сообщений в файл. Для этого:
1. Перейдите в нижнюю область окна Windows Messages (Сооб-
щения Windows) и вызовите локальное меню.
2. Выберите команду Send To Log Window (Пересылка в окно ре-
гистрации). Если эта установка имеет значение No (Нет),
переключите ее на Yes (Да), нажав клавишу Enter.
3. Перейдите в окно Log (Регистрация), выбрав команду
View¦Log (Обзор¦Регистрация), и вызовите локальное меню.
4. Выберите команду Open Log File (Открыть файл регистрации)
и нажмите клавишу Enter, чтобы подтвердить имя файла ре-
Теперь вы готовы запустить программу,
гистрации (журнала) по умолчанию, BCWDEMOA.LOG.
Теперь вы готовы запустить программу, нажав клавишу F9. Ког-
да появится экран, нажмите клавиши Ctrl-Alt-SysRq, чтобы вернуть-
ся к TDW.
Анализ протокола сообщений
-----------------------------------------------------------------
В нижней области окна Windows Messages (Сообщения Windows)
вы увидите множество сообщений WM_PAINT, и вероятно, больше ника-
ких других. Для того, чтобы просмотреть все сообщения, выберите
команду View¦File (Обзор¦Файл) и получите список всех файлов в
текущем каталоге. Выберите из этого списка файл BCWDEMOA.LOG,
представляющий собой протокол сообщений.
При этом вы получите достаточно короткий список сообщений
инициализации окна (16 или около этого), за которым следует очень
длинный список сообщений WM_PAINT. причиной является то, что
Windows начала посылать сообщения, чтобы установить исходный эк-
ран, но "застряла" на сообщении WM_PAINT. Данный анализ отражает
то, что вы видели при запуске программы: экран появился, но боль-
ше ничего не происходило.
TDeb 3.0 #3-3 = 51 =
Поиск ошибки
-----------------------------------------------------------------
Итак, что делать дальше? Можно начать с просмотра кода, что-
бы найти там тот, участок, который отвечает за обработку
WM_PAINT. Однако, существует более привлекательная альтернатива,
которая состоит в том, чтобы установить точку останова по сообще-
нию, позволяющую сразу выйти на нужный участок программы, а затем
начать пошаговое выполнение, которое позволит точно выяснить при-
чину ошибки.
Установка точки останова по сообщению также отменит действие
выхода по Ctrl-Alt-SysRq, который делает небезопасным дальнейшее
пошаговое выполнение программы или выход
пошаговое выполнение программы или выход из TDW. Так как сообще-
нием, на котором "застряла" программа, является WM_PAINT, устано-
вите для TDW прерывание по сообщению WM_PAINT и снова запустите
программу, следующим образом:
1. Снова войдите в окно Windows Messages (Сообщения
Windows), перейдите в правую верхнюю область, вызовите
локальное меню и выберите Add (Добавление).
2. Появится блок диалога Set Message Filter (Задание фильтра
сообщений) с уже выбранным значением Single Message (От-
дельное сообщение), а курсор в это время будет находиться
в текстовом поле ввода Single Message Name (Имя отдельно-
го сообщения). Введите WM_PAINT (только заглавными бук-
вами, иначе TDW не сможет найти соответствие), а в ка-
честве действия выберите Break (Прерывание).
3. Для запуска программы нажмите F9.
Программа немедленно прервется, и вы окажитесь на первой
строке WndProc. (Чтобы получить полный обзор кода, вы должны
очистить с экрана окно Windows Messages). Эта подпрограмма состо-
ит из оператора switch для сообщений, специальным образом обраба-
тываемых программой.
Подпрограмма WndProc:
long FAR PASCAL WndProc (HWND hWnd, unsigned Message,
WORD wParam, LONG lParam)
{
switch(Message)
{
case WM_COMMAND:
return DoWMCommand(wParam, hWnd);
case WM_LBUTTONDOWN:
DoLButtonDown(hWnd,lParam);
break;
case WM_LBUTTONUP:
DoLButtonUp(hWnd,lParam);
break;
TDeb 3.0 #3-3 = 52 =
case WM_MOUSEMOVE:
DoMouseMove(hWnd,lParam);
Для пошагового выполнения программы нужно
break;
case WM_PAINT:
DoPaint(hWnd);
break;
default:
return DefWindowProc(hWnd,Message,wParam,lParam);
}
return 0;
}
Пошаговое выполнение программы
-----------------------------------------------------------------
Для пошагового выполнения программы нужно нажимать клавишу
F7. Маркер текущей строки дойдет до оператора case WM_PAINT, и
наконец до подпрограммы DoPaint.
Подпрограмма DoPaint:
void DoPaint(HWND hWnd)
{
int i,
saveROP;
HDC hdc,
hMemDC;
RECT theRect,
destRect;
HBITMAP the Bitmap;
PAINTSTRUCT ps;
if (CurrentPoint >= 0)
{
hdc = BeginPaint(hWnd,&ps);
/*
* Определить, какая прямоугольная область отмечена
* как недопустимая.
* Если ни один прямоугольник не помечен как
* недопустимый, то экран будет полностью перерисован.
*/
GetUpdateRect(hWnd,&theRect,0);
if (IsRectEmpty(&theRect))
GetClientRect(hWnd,&theRect);
/*
* Создание DC (контекста устройства) и области
* того же размера, что и обновляемый прямоугольник.
*/
TDeb 3.0 #3-3 = 53 =
hMemDC = CreateCompatibleDC(hdc);
theBitmap = CreateCompatibleBitmap(hdc,
theRect.right-theRect.left
theRect.bottom-theRect.top);
SelectObject(hMemDC,theBitmap);
/*
Рисование только тех фигур, которые
* Стирание memBitmap
*/
BitBlt(hMemDC, 0, 0,
theRect.right-theRect.left
theRect.bottom-theRect.top,
hdc, 0, 0, SCRCOPY);
/*
* Рисование только тех фигур, которые находятся
* внутри обновляемого прямоугольника.
*/
for (i = 0; i <= CurrentPoint; ++i)
{
IntersectRect(&destRect, &thisShape[i].Points,
&theRect);
if (!IsRectEmpty(&destRect))
DrawShape(hMemDC,
thisShape[i].Points.left-theRect.left,
thisShape[i].Points.top-theRect.top,
thisShape[i].Points.right-theRect.left,
thisShape[i].Points.bottom-theRect.top,
thisShape[i].theShape,thisShape[i].PenWidth,
thisShape[i].PenColor,thisShape[i].slope);
/*
* Отметим, что при рисовании фигуры программа переместила
* ее точку начала координат т.о., что она оказалась в
* верхнем левом углу обновляемого прямоугольника.
* Это точка (0,0) области, в которую будет выполнено
* отображение (theRect.left,theRect.right).
*/
}
/*
* И наконец, копирование области в область обновляемого
* прямоугольника.
*/
BitBlt(hdc, theRect.left, theRect.top,
theRect.right-theRect.left,
theRect.bottom-theRect.top,
hMemDC, 0, 0, SRCCOPY);
DeleteDC(hMemDC);
TDeb 3.0 #3-3 = 54 =
DeleteObject(theBitmap);
EndPaint(hWnd,&ps);
По мере продолжения пошагового выполнения
}
}
По мере продолжения пошагового выполнения вы увидите, что
единственная строка кода, выполняемая внутри DoPaint, это:
if (CurrentPoint >= 0)
Затем управление возвращается в цикл сообщений, в котором
программа принимает следующее сообщение, WM_PAINT, и затем снова
уходит на цикл с WndProc и DoPaint. Подпрограмма DoPaint, безус-
ловно, что-то делает не так, и нужно сначала выяснить, что же она
должна делать на самом деле?
TDeb 3.0 #3-3 = 55 =
Анализ DoPaint
-----------------------------------------------------------------
Назначение данной подпрограммы состоит либо в рисовании все-
го экрана при первом вызове подпрограммы, либо в перерисовке об-
ласти экрана, текущего прямоугольника, если на экране было
что-либо нарисовано. Чтобы определить, было ли что-нибудь нарисо-
вано, DoPaint проверяет значение переменной Currentpoint, перво-
начально устанавливаемой в -1. (CurrentPoint указывает число на-
рисованных объектов). Если CurrentPoint имеет значение -1, то
есть значение, которое было установлено при запуске и рисовании
исходного экрана, то брать и перерисовывать содержимое текущего
прямоугольника нет необходимости, поэтому все коды внутри опера-
тора if опускается, и происходит возврат, а Windows перерисовыва-
ет все окно.
Если вы будете проверять значение CurrentPoint в окне
View¦Watches (Обзор¦Просмотр), то вы увидите, что оно остается
равным -1 при пошаговом прохождении программы. Это так и должно
быть, поскольку нарисовать что-либо вы не имели возможности.
Нахождение ошибки
-----------------------------------------------------------------
Теперь, если вы обратитесь к книге Чарльза Петцольда "Прог-
то причина ошибки станет вам
раммирование для Windows" ("Charles Petzold, Programming
Windows"), то причина ошибки станет вам ясна. Минимальная реак-
ция, которая нужна Windows в ответ на сообщение WM_PAINT, должна
состоять в вызове BeginPaint, за которым следует EndPaint. Если
эти подпрограммы не вызываются, то Windows не знает, что было по-
лучено сообщение WM_PAINT, и продолжает посылать WM_PAINT прог-
рамме.
Обратите внимание, что вызов BeginPaint находился внутри
оператора if, и подпрограмма при первой прорисовке экрана не вы-
зывалась. Чтобы решить проблему, вы должны вынести оператор с вы-
зовом BeginPaint за пределы условного оператора if.
Как обстоит дело с оператором EndPaint? Он тоже находится
внутри if, вместе с вызовом подпрограммы ReleaseDC, которая осво-
бождает hdc, логический номер (описатель) контекста устройства,
устанавливаемый вызовом BeginPaint. Эти две строки должны нахо-
диться вне конструкции if.
Завершение BCWDEMOA
-----------------------------------------------------------------
В BCWDEMOB, второй версии программы, которую вы будете изу-
чать, ошибка, связанная с WM_PAINT, уже исправлена. Прежде чем
загружать эту программу, нужно завершить работу BCWDEMOA, чтобы
освободить используемые ей ресурсы. Поскольку единственная проб-
лема с этой программой состояла в том, что при первом проходе не
выполнялся код внутри оператора if, установка CurrentPointer в
TDeb 3.0 #3-3 = 56 =
ноль приведет к выполнению этого кода, что позволяет нормально
выйти из программы. Для завершения программы выполните следующие
шаги:
1. В окне Windows Messages (Сообщения Windows) уберите имя
оконной процедуры WndProc в левой верхней области, чтобы
Нажимайте F7, пока на дисплее
программа не была прервана при получении сообщения
WM_PAINT.
2. Нажимайте F7, пока на дисплее не появится следующая стро-
ка:
if (Currentpointer >= 0)
3. Выделите CurrentPoint, затем нажмите клавиши Ctrl-F4,
чтобы перейти к экрану Evaluate/Modify (Вычисление/Моди-
фикация).
4. Выберите Eval (Вычисление).
5. Поместите курсор на поле ввода New Value (Новое значе-
ние), введите 0, а затем выберите Modify (Модификация),
чтобы изменить значение переменной. Теперь при выполнении
программы оператор if при вычислении условия даст значе-
ние True, а BeginPaint и EndPaint будут выполнены при
первом же проходе.
6. Выберите клавишу F9 для выполнения программы.
7. Выберите Quit для выхода из программы. Чтобы эта команда
сработала, вам может понадобиться нажать клавишу, пос-
кольку Windows может отказаться освободить сообщения вво-
да от "мыши" из системной очереди. Нажатие клавиши осво-
бодит из очереди все находящиеся там в текущий момент со-
общения ввода от "мыши".
Отладка BCWDEMOB
-----------------------------------------------------------------
Завершив работу программы BCWDEMOA, вы можете загрузить
программу BCWDEMOB. Когда программа появится на экране, нажмите
для ее запуска клавишу F9, и попробуйте немного поработать с
программой.
Если вы нарисовали большое число объектов, и особенно при
интенсивном перемещении "мыши" при нажатой кнопке, вы увидите в
конце-концов, что с программой начинают происходить странные ве-
щи. Сначала вы сможете заметить,что программа стала медленнее ра-
а затем объекты исчезают или
ботать, а затем объекты исчезают или изменяется цвет или толщина
линий, а экраны перемешиваются. И наконец, все зависает, и вам
приходится перезагружаться аппаратным образом.
Наиболее вероятная причина состоит в ошибочном использовании
TDeb 3.0 #3-3 = 57 =
памяти. Чтобы проверить, так ли это, и найти местоположение ошиб-
ки, повторите загрузку Windows, выполните только менеджер прог-
рамм и TDW, если это возможно (чтобы минимизировать использование
памяти), и загрузите BCWDEMOB и TDW снова.
Переключение из программы
-----------------------------------------------------------------
Прежде чем продолжить сеанс отладки, может потребоваться ор-
ганизовать отладку таким образом, чтобы не использовать
Ctrl-Alt-SysRq для переключения из перекладной программы в TDW,
поскольку использование данного метода требует осторожности. Ре-
шение состоит в том, чтобы установить единственное сообщение, по
которому должно выполняться прерывание, и которое вы по своему
желанию можете генерировать в программе, и которое обычно само по
себе не появляется.
Так как BCWDEMO - это работающая с "мышью" графическая прог-
рамма, не принимающая графического ввода, то работа с клавиатурой
здесь обычно не нужна. Следовательно, лучше всего организовать
прерывание по сообщению WM_KEYFIRST. После задания этого сообще-
ния в окне View¦Windows Message (Обзор¦Сообщения Windows) прог-
рамма будет прерываться при каждом нажатии клавиши. (Поскольку
программа не реагирует и на нажатие правой кнопки "мыши", для
этой цели можно использовать и сообщение WM_RBUTTONDOWN).
Тестирование программы
-----------------------------------------------------------------
Теперь вы можете нажать клавишу F9, чтобы начать выполнение
программы BCWDEMOB. Прежде чем сделать
программы BCWDEMOB. Прежде чем сделать что-либо в программе, про-
верьте, какой процент системной памяти доступен программам для
Windows, переключившись на менеджер программ, выбрав "кнопку"
Help (Справка) и затем выбрав About Program Manager (О менеджере
программ). Появится информационный блок, в котором будет показана
текущая версия Windows, объем свободной памяти и в нижней части -
интересующие вас статистические данные, процентные соотношения
свободной памяти для системных ресурсов.
Теперь вернемся в программа Simple Paint и немного порисуем.
Затем снова перейдем на экран About Program Manager (О менеджере
программ), и вы увидите, что доступная память для системных ре-
сурсов уменьшилась. Если вы и дальше продолжите рисование, то
объем этой памяти будет продолжать уменьшаться до нуля, и вы по-
лучите тот же эффект, что и ранее.
Принятие решения
-----------------------------------------------------------------
Что вам известно в данный момент? Прежде всего, программа
явно распределяет глобальную память, так как она в результате
расходует все системные ресурсы, доступные Windows. С этого можно
начать: вы можете перезагрузить программу, воспользоваться средс-
TDeb 3.0 #3-3 = 58 =
твами TDW контроля глобальной памяти для сохранения списка объек-
тов глобальной памяти в файл регистрации (протокол), немного по-
рисовать в Simple Paint и затем снова получить список объектов
глобальной памяти, записав его в другой файл.
Так как Windows за это время, вероятно, перемещала объекты в
памяти, то два полученных списка могут не совпадать. Вы должны
просмотреть каждый объект в списке и сопоставить его с объектами,
имеющими того же владельца в другом списке, чтобы найти несоот-
Поскольку этот метод занимает много
ветствия.
Поскольку этот метод занимает много времени, то предлагается
он только как упражнение по просмотру глобальной памяти. Метод,
которым вы будете пользоваться фактически, состоит в том, чтобы
просмотреть объекты памяти, выделяемые программой, и проверить,
выполняет ли программа и их освобождение.
Сравнение списков объектов глобальной памяти
-----------------------------------------------------------------
Для получения списка объектов глобальной памяти нужно выпол-
нить следующие действия:
1. Перезапустите Windows, чтобы обеспечить очистку глобаль-
ной памяти от любых объектов, выделенных BCWDEMOB. И
опять, избегайте запускать ненужные программы, чтобы
уменьшить число объектов глобальной памяти.
2. Если вы находитесь в TDW и загрузили BCWDEMOB, снова
установите прерывание программы по сообщению WM_KEYFIRST.
3. Нажмите клавишу F9 для запуска программы, а затем нажмите
клавишу для выхода обратно в TDW.
4. Выберите команду View¦Log (Обзор¦Регистрация) и вызовите
локальное меню.
5. Выберите команду Open Log¦File (Открыть файл¦Регистра-
ция), введите в появившемся окне диалога имя файла журна-
ла и нажмите клавишу Enter.
6. Снова вызовите локальное меню окна Log (Регистрация), вы-
берите команду Display Windows Info (Вывод информации
Windows), а затем нажмите клавишу Enter, когда появится
окно диалога Windows Information (Информация Windows).
Нажатие Enter принимает установки по умолчанию, задающие
вывод списка объектов глобальной памяти, начиная с верх-
них адресов памяти.
7. Когда отладчик TDW закончит листинг глобальной памяти,
снова вызовите список объектов глобальной
снова вызовите список объектов глобальной памяти и выбе-
рите команду Close Log File (Закрытие файла регистрации),
чтобы закрыть файл.
TDeb 3.0 #3-3 = 59 =
8. Выберите из локального меню команду Erase Log (Стереть
протокол), чтобы очистить протокол.
9. Нажмите F9, чтобы снова запустить программу, и при помощи
"мыши" проконтролируйте информацию в блоке About Program
Manager. Запомните проценты, приведенные там для систем-
ных ресурсов.
10. Нарисуйте в Simple Paint достаточно, чтобы уменьшить сис-
темные ресурсы на 20-30 процентов.
11. Нажмите клавишу для возврате в TDW и повторите шаги 4-8,
на этот раз с другим именем файла протокола (регистра-
ции).
12. Выйдите из TDW, распечатайте файлы протоколов и сравните
их.
Проделав это, вы отметите следующее:
- Объекты памяти, принадлежащие BCWDEMOB, не увеличились в
размерах.
- Объекты памяти GDI увеличились в размерах.
Первое из этих примечаний подтверждает то, что вы уже знаете
о программе: отлаживаемый код программы BCWDEMOB выделяет гло-
бальную память, а не локальную.
Второе говорит вам нечто новое: BCWDEMOB выделяет объекты
Интерфейса графических устройств (GDI) и не освобождает их.
Нахождение ошибки: функциональный подход
-----------------------------------------------------------------
Теперь, зная характер ошибки, вы можете начать искать место
в программе, где выделяются объекты памяти, не освобождаемые
впоследствии. Для этого полезно сделать функциональный обзор
программы и исследовать каждую подпрограмму в последовательности
их вызова.
Выбор элементов меню
Выбор элементов меню выполняется посредством
-----------------------------------------------------------------
Выбор элементов меню выполняется посредством помещения кур-
сора в меню, нажатия левой кнопки "мыши" и перемещения по меню к
желаемому элементу, выбор которого изменяет цвет, толщину пера
или форму изображения. Изменение любого из этих значений вызывает
посылку сообщения WM_COMMAND в подпрограмму WndProc, которая об-
рабатывает сообщение, вызывая для этого DoWMCommand.
DoWMCommand содержит оператор switch, который сохраняет сде-
TDeb 3.0 #3-3 = 60 =
ланный вами выбор в переменной программы. Эти переменные хранятся
в сегменте данных BCWDEMO и не влияют на глобальную память.
Рисование фигуры
-----------------------------------------------------------------
Для того, чтобы нарисовать фигуру, вы устанавливаете курсор
в предназначенную для графического ввода пользователя область ок-
на, удерживая нажатой левую кнопку "мыши" перемещаете курсор в
другую точку и там отпускаете кнопку "мыши". Если вы перемещаете
"мышь" при ненажатой левой кнопке, вы можете заметить, что фигура
по мере перемещения рисуется, стирается и снова рисуется. Остает-
ся она на экране только при отпускании левой кнопки.
Нажатие левой кнопки "мыши"
-----------------------------------------------------------------
При нажатии левой кнопки в области пользовательского графи-
ческого ввода Windows посылает сообщение WM_LBUTTONDOWN в
WndProc, что приводит к вызову DoButtonDown. Эта подпрограмма
сохраняет текущую позицию "мыши" (которая далее именуется
меткой) и выполняет установки характеристик пера в структуре
thisShape. Эта структура представляет собой переменную программы
и влияет только на сегмент
и влияет только на сегмент данных программы BCWDEMO.
Перемещение "мыши"
-----------------------------------------------------------------
Когда вы перемещаете "мышь" при нажатой левой кнопке в поль-
зовательской области ввода, Windows посылает WM_MOUSEMOVE (или WM
_MOUSEFIRST, что то же самое) в WndProc, которая вызывает
DoMouseMove. Эта подпрограмма вызывает DrawShape для стирания фи-
гуры от предыдущей позиции "мыши" до исходной, а затем снова -
для рисования фигуры от текущей позиции до метки. Единственное
использование глобальной памяти в DoMouseMove состоит в получении
контекста устройства для текущего окна, который освобождается в
конце подпрограммы вызовом ReleaseDC.
Рисование фигуры (и нахождение позиции ошибки)
-----------------------------------------------------------------
Теперь рассмотрим подпрограмму DrawShape, которая дважды вы-
зывается из DoMouseMove. DrawShape запоминает перо, которым рисо-
валась предыдущая фигура, создает новое перо и рисует линию, эл-
липс или прямоугольник. В конце своей работы она восстанавливает
сохраненное на входе перо.
Поскольку перо - это объект интерфейса графических устройств
(GDI), выделяемый в глобальной памяти, подпрограмма DrawShape мо-
жет содержать код, вызывающий проблемы с памятью. Эта подпрограм-
ма вызывает особенное подозрение, так как она вызывается дважды
при каждом перемещении мыши. Если она создает перья и не удаляет
их, то она "съест" память очень скоро.
TDeb 3.0 #3-3 = 61 =
Действительно, недалеко от начала DrawShape выделяет перо
вызовом SelectObject, но не освобождает выделенную ему память вы-
зовом DeleteObject в конце. Чтобы исправить эту ошибку, вы должны
заменить последнюю строку DrawShape следующим
заменить последнюю строку DrawShape следующим кодом:
DeleteObject(SelectObject(hdc,saveObject));
Возможно, найденная ошибка является единственной причиной
проблем с памятью, однако желательно проверить и все остальные
места программы, связанные с рисованием объектов.
Отпускание левой кнопки
-----------------------------------------------------------------
При отпускании левой кнопки "мыши" BCWDEMOB рисует фигуру в
последний раз и оставляет ее на экране. Отпускание кнопки застав-
ляет Windows послать сообщение WM_LBUTTONUP в WndProc, которая
вызывает DoLButtonUp. Эта подпрограмма сохраняет текущий прямоу-
гольник из пользовательской области в массив текущей фигуры
thisShape, вызывает InvalidateRect для добавления области в об-
ласть обновления окна, а затем вызывает UpdateWindow, которая по-
сылает сообщение WM_PAINT прямо в главное окно. Эта подпрограмма
не использует глобальную память.
При выходе из DoLButtonUp сообщение WM_PAINT находится в
очереди и готово к обработке в WndProc.
TDeb 3.0 #3-3 = 62 =
Перерисовка экрана
-----------------------------------------------------------------
Когда WndProc получает сообщение WM_PAINT, она вызывает
DoPaint для перерисовки соответствующей области экрана (описана
выше в этом разделе). При перерисовке обновляемого прямоугольника
DoPaint вызывает две подпрограммы Windows, влияющие на глобальную
память: CreateCompatibleDC и SelectObject. В конце DoPaint вызы-
ваются DeleteDC и DeleteObject, которые освобождают выделенную в
начале подпрограммы память.
Заключение
-----------------------------------------------------------------
Так как вы рассмотрели все подпрограммы, вы можете быть уве-
что вы недостаточно детально знаете
рены, что ошибка, связанная с памятью, найдена. Этот метод был
выбран потому, что вы недостаточно детально знаете отлаживаемую
программу. Разумеется, в программе, написанной лично вами, вы
нашли бы такую ошибку гораздо быстрее.
Не мешает также и более подробное тестирование программы.
Хорошо зная программу, вы могли бы не искать ошибку в части прог-
раммы, связанной с меню, так как обнаружили бы, что сбой програм-
мы наступает и при одном только перемещении курсора по экрану с
помощью "мыши". Кроме того, обнаружив, что проблема возникает
только при перемещении "мыши" в области пользовательского графи-
ческого ввода при нажатой левой кнопке (это вы могли бы обнару-
жить, нажав кнопку и перемещая курсор до тех пор, пока не прои-
зойдет сбой программы), вы могли бы сразу предположить, что
проблема связана с подпрограммой DoMouseMove и на ней сосредото-
чить свои усилия.
TDeb 3.0 #3-3 = 63 =
Глава 19. Отладка программы, использующей ObjectWindows
-----------------------------------------------------------------
Пример объектно-ориентированной программы данной главы был
написан с помощью класса ObjectWindows, который существенно об-
легчает программирование в Windows.
Примерами программ служат программа TDODEMO и TDODEMOB (B
обозначает версию программы с ошибками). Программа TDODEMOB со-
держит несколько ошибок, которые вы выявите при работе с данной
главой.
Перед тем, как продолжить изучение, полезно запустить из
Windows программу TDODEMO и немного поэкспериментировать с ней,
чтобы получить представление, как она работает. Вы можете исполь-
зовать для запуска TDODEMO.EXE команду File¦Run (Файл¦Выполнение)
менеджера программ или добавить ее к программной группе в качест-
и TDODEMOB вам потребуется открыть
ве пиктограммы.
Примечание: При отсутствии файлов .EXE для файлов
TDODEMO и TDODEMOB вам потребуется открыть их файлы проектов
и перекомпилировать эти файлы с включением отладочной инфор-
мации.
О программе
-----------------------------------------------------------------
TDODEMOB - это программа, написанная с использованием
ObjectWindows, позволяющая рисовать на экране различными цветами
с помощью "мыши". Когда вы нажимаете левую кнопку "мыши" и пере-
мещаете "мышь", то программа рисует на экране линию. Нажав правую
кнопку "мыши", вы можете очистить окно. Программа TDODEMO имеет
строку меню, которая позволяет вам выбрать один из 4 цветов: Red
(красный), Green (зеленый), Blue (голубой) или Black (черный).
Вы можете рисовать, нажимая кнопку "мыши", перемещая "мышь"
и освобождая кнопку "мыши". Программа легко выполняет эту задачу
с помощью библиотеки ObjectWindows и динамических виртуальных ме-
тодов. Динамический виртуальный метод - это метод с присвоенным
ему числовым идентификатором.
Поскольку Borland C++ определяет имена сообщений Windows,
как числовые константы, вы можете использовать номера сообщений
Windows в качестве идентификатора динамического метода. При этом
ObjectWindows может вызывать данный метод, когда окно, для кото-
рого объявлен метод, получает совпадающее с идентификатором мето-
да сообщение. Если нет метода, идентификатор которого совпадает с
идентификатором метода, ObjectWindows вызывает используемую по
умолчанию процедуру окна.
Например, чтобы создать метод, который отвечает на сообщения
WM_MOUSEMOVE, вы можете определить метод в объекте окна, который
который непосредственно следует за описанием
выглядит следующим образом:
TDeb 3.0 #3-3 = 64 =
procedure WMMouseMove(var Msg: TMessage); virtual WM_MOUSE;
Как вы можете видеть, идентификатор WM_MOUSEMOVE можно при-
соединить к процедуре с помощью оператора virtual <идентифика-
тор>, который непосредственно следует за описанием процедуры.
Параметры процедуры окна Windows wParam и lParam содержит
тип TMessage. Эти параметры часто содержат дополнительную инфор-
мацию о сообщении, например, где позиционируется "мышь".
В следующих нескольких разделах поясняется, как работает
программа TDODEMOB. В нее преднамеренно внесены ошибки, которые
вы сможете обнаружить позднее. Полезно также запустить Турбо Пас-
каль для Windows и открыть файл TDODEMOB.PAS, после чего вы смо-
жете следовать по коду программы.
Определение оконного типа ScribbleWindow
-----------------------------------------------------------------
Тип ScribbleWindow определяется следующим образом:
class ScribbleWindow : public TWindow
{
public:
HDC HandleDC; // вывод содержимого для рисования
BOOL ButtonDown; // флаг левой кнопки
HPEN ThePen; // перо, которое используется для
// рисования цветом
ScribbleWindow(PTWindowObject AParent, LPSTR ATitle);
-ScribbleWindow();
void GetWindowClass(WNDCLASS &AWndClass);
virtual void WMLButtonDown(RTMessage
Msg)=[WN_First+WM_LBUTTONDOWN];
virtual void WMLButtonUp(RTMessage MSG)=(WM_FIRST+
WM_LBUTTONUP);
virtual void WMLMouseMove(RTMessage MSG)=(WM_FIRST+
WM_MOUSEMOVE);
virtual void WMLButtonDown(RTMessage MSG)=(WM_FIRST+