
Роббинс Д. - Отладка приложений для Microsoft .NET и Microsoft Windows - 2004
.pdf

604 |
ЧАСТЬ IV Мощные средства и методы отладки неуправляемого кода |
|||
|
|
|
|
|
|
|
|
|
|
|
|
if ( ( m_iKeyBuffKeys > 20 |
) || |
|
|
|
( m_cKeyBuff.Length ( ) > 50 ) ) |
|
|
|
|
{ |
|
|
|
|
DoKeyStrokes ( TRUE ) ; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
break ; |
|
|
|
|
} |
|
|
|
|
// Установка состояния, в которое выполняется |
|
|
|
|
// переход после обработки этой клавиши. |
|
|
|
|
eCurrKeyState = eShiftToState ; |
|
|
|
} |
|
|
|
|
|
|
|
|
Специальным образом обрабатывается только комбинация Alt+Tab. Я мог за писывать фактические нажатия клавиш Alt и Tab, однако это могло бы привести к проблемам при следующем запуске сценария, так как мне нужно было бы раз мещать окно приложения в том же месте z порядка и иметь то же число запущенных приложений. Поэтому вместо записи нажатий клавиш я перехожу в состояние ожидания того, когда вы отпустите клавишу Alt, а при следующем вводе какой либо журнальной информации я определяю, какое приложение имеет фокус, и гене рирую соответствующий код сценария.
Обработка ввода мыши
Когда я собрался реализовать поддержку мыши, я по настоящему удивился тому, что мой первоначальный механизм обработки событий клавиатуры не позволял добавить обработку событий мыши. Первоначальный вариант моего кода обра ботки клавиатуры оптимизировал обработку Ctrl, Shift и Alt, гарантируя, что один метод PlayInput включает полную команду, начиная с нажатия одной из клавиш Ctrl, Shift или Alt и заканчивая ее отпусканием. Когда я стал думать о поддержке событий мыши, я понял, что это может приводить к генерированию метода Play7 Input, включающего десятки тысяч символов! Даже для начала обработки ввода мыши мне нужно было изменить код записи событий клавиатуры, чтобы он ге нерировал специальные коды, такие как {ALT DOWN} и {ALT UP}. Это было нужно для того, чтобы сгенерированные команды мог выполнять любой язык сценариев.
Позаботившись об обработке Ctrl, Alt и Shift, я должен был разобраться с оди ночными, двойными щелчками и перетаскиванием. Очень интересно, что ловуш ка записи журнала, при помощи которой я регистрирую события клавиатуры и мыши, получает только сообщения WM_xBUTTONDOWN и WM_xBUTTONUP. Я был бы очень рад получать сообщения WM_xBUTTONDBLCLK, так как это сделало бы мою жизнь го раздо проще. Обработка событий мыши отчаянно требовала создания конечного автомата, подобного тому, что я разработал для обработки событий клавиатуры. На рис. 16 5 и 16 6 показан конечный автомат обработки ввода мыши, который я реализовал в файлах RECORDINGENGINE.H/.CPP. Помните, что я должен был вы полнять такое отслеживание состояния для каждой клавиши. Слоты 0 и 1 нужны для слежения за предыдущим событием с целью сравнения.


606 |
|
|
ЧАСТЬ IV Мощные средства и методы отладки неуправляемого кода |
|||||||||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Посмотреть |
|
|
|
|
|
|
|
|
|
|
|
|
|
||||
состояние DBL CLK |
|
|
|
|
Возврат к |
|
просмотру |
|
|
|
|
|
||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||||
|
|
Любой ввод |
|
|
|
|
|
|
|
|
|
|
||||||
|
|
|
|
|
|
|
|
|
состояния DBL CLK |
|
|
|
|
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Посмотреть |
|
|
|
|
|
|
|
|
|
|
|
|
Генерирование |
|
|
|
||||
|
|
Тип ввода |
|
Движение |
|
|
события движения |
|
|
|
состояние |
|||||||
|
|
|
|
|
(если установлен |
|
|
|
отпускания |
|||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||||
|
|
|
|
|
|
|
|
|
|
|
||||||||
|
|
|
|
|
|
|
|
|
|
|
параметр) |
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Нажата |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||
|
та же кнопка |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
Время > |
|
|
|
|
|
|
|
|
|||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||||
|
|
|
|
|
|
|
|
|
Генерирование |
|
|
|
|
|
||||
|
|
Проверить |
|
|
|
|
|
Очистить |
|
Сохранить |
||||||||
|
|
|
|
времени |
|
|
|
события щелчка |
|
|
||||||||
|
|
время |
|
|
|
|
|
слоты 0, 1 |
|
событие в слоте 0 |
||||||||
|
|
|
DBL CLK |
|
|
|
для слотов 0, 1 |
|
|
|||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Время < |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
времени DBL |
CLK |
|
|
|
|
|
|
|
|
|
|
|
|
|
Щелчок в пределах двойного Нет
щелчка?
Да
Генерирование
события двойного щелчка
Очистить слоты 0, 1
Начальное
состояние
Рис. 16 6. Конечный автомат обработки двойного щелчка
После написания кода записи событий мыши все казалось правильным, пока я не занялся серьезным тестированием — сразу же возникли проблемы. Запись сценариев рисования в Microsoft Paint работала отлично, но при их воспроизве дении я столкнулся с неприятностями. Например, при воспроизведении нарисо ванной вручную окружности сначала появлялась прямая линия, а затем вырисо вывалась оставшаяся часть окружности. Я тщательно изучил код записи и воспро изведения сценариев, но ошибок не нашел. Как оказалось, сценарий слишком бы
ГЛАВА 16 Автоматизированное тестирование |
607 |
|
|
стро передавал команды MOVETO, что вызывало переполнение входной очереди ОС Windows и отбрасывание избыточных сообщений. Следовательно, я должен был замедлить обработку сообщений мыши, обеспечив достаточное время для выпол нения всех соответствующих событий. Так как для воспроизведения команд я использовал функцию SendInput, я сначала подумал о том, чтобы задать время для каждого события мыши в структуре INPUT, предоставив дополнительное время для их обработки. Это не сработало, и я обнаружил, что задание достаточно долгого времени переводит компьютер в режим энергосбережения, что в первый раз меня довольно сильно удивило.
Тогда я попробовал другой способ. Я решил, что, если мой код записывает вво димые команды в массив структур INPUT, указатель на который передается функ ции SendInput, я могу изучать массив по одному элементу и делать дополнитель ную паузу при обнаружении событий мыши. Длительность пауз я определил экс периментально. После ряда проб я обнаружил, что лучше всего делать паузы на 25 миллисекунд до и после каждого события мыши. Это означает, что записан ные сценарии будут воспроизводиться гораздо медленнее по сравнению с тем, когда вы их записывали.
Что после Tester?
Как я уже говорил, Tester хорошо справляется с двумя вещами: с записью сцена риев и воспроизведением записанных событий. Если у вас имеется необходимое вдохновение, вы можете усовершенствовать Tester (равно как и все остальные утилиты из этой книги). Вот некоторые возможные способы улучшения Tester:
Добавьте классы оболочки, такие как TListBox, TTreeControl и TRadioButton, что бы вы могли проверять состояния и данные элементов управления. Такие классы позволят проверять элементы управления и писать более сложные сценарии. Возможно, для облегчения этой задачи понадобится изучить интерфейсы MSAA.
Реализуйте поддержку оперативного ввода проверочного и другого необходи мого кода в сценарий во время его записи.
Сделайте программу TestRec более дружественной к пользователю во время разработки сценариев и создайте систему помощи. Например, можно реали зовать хранение сведений о поддерживаемых объектах Tester, что поможет другим разработчикам писать собственные сценарии.
Реализуйте возможность записи сценариев на компилируемых языках, таких как C# и Visual Basic .NET (или Microsoft Visual Object Assembler, если Microsoft когда нибудь его создаст).
В настоящее время TestRec и Tester поддерживают только клавиатуру US English. Если хотите, вы можете сделать оба приложения по настоящему интернацио нальными.

608 ЧАСТЬ IV Мощные средства и методы отладки неуправляемого кода
Резюме
Блочное тестирование UI иногда вызывает проблемы. В этой главе я представил полезную утилиту, Tester, которая позволяет автоматизировать тестирование, за писывая манипуляции с клавиатурой и мышью и проигрывая их вашему прило жению. По функциональности Tester немного не дотягивает до коммерческих средств регрессивного тестирования, но даже в этом случае он благоприятно ска жется на ваших запястьях.
Надеюсь, Tester покажет вам, насколько ценными могут быть средства автома тизации выполнения программ. Если ваше приложение довольно сложно, я реко мендую приобрести коммерческое средство регрессивного тестирования, чтобы ускорить проведение блочного тестирования. Потратив некоторое время на пла нирование использования средства регрессивного тестирования, вы сможете со здать систему, позволяющую программистам писать сценарии, которые сотруд ники отдела контроля качества смогут включать в автоматизированные тесты контроля качества всей программы. Если вы сделаете все правильно, вам будет казаться, что основные тесты контроля качества пишут себя сами.

Г Л А В А
17
Стандартная отладочная библиотека C и управление памятью
Даже после выхода первого издания этой книги я получал и получаю массу во просов по поводу искажений и утечек памяти. Если бы программисты просто пре кратили использовать память в своих программах, они избежали бы массы про блем. Все так. А если бы мы прекратили дышать, мы никогда не страдали бы от легочных болезней. Память — эликсир жизни программ C и C++, поэтому, если вы и впрямь хотите что то сделать с искажениями и утечками памяти, а не про сто мечтать, чтобы они исчезли, нужно позаботиться об их проактивной обра ботке. Первый шаг в этом направлении — изучение отладочной библиотеки C, разработанной Microsoft.
Сложность отладки памяти имеет легендарный статус, и именно она была од ним из главных факторов, побудивших Microsoft разработать платформу .NET. Благодаря сборщику мусора CLR программисты могут избавиться от многих ас пектов работы с памятью, что устраняет, наверное, около 50% ошибок, с которы ми приходится сталкиваться в мире Microsoft Win32/Win64. Однако, если для ва ших приложений очень важно быстродействие, вам еще долго придется писать их на C++ и быть готовым к возможным ошибкам при работе с памятью.
C/C++ программисты от таких проблем ничем не защищены. Эти языки обес печивают почти полную свободу программирования, но при этом позволяют вам не только выстрелить себе в ногу, но и полностью отстрелить ее даже при неболь шой ошибке. К счастью, разработчики стандартной библиотеки C (C run time, CRT library) не оставили наши муки без внимания и создали миллион Интернет лет назад удивительное средство — отладочную библиотеку CRT (debug CRT, DCRT library), включенную в состав сред Microsoft, начиная с Visual C++ 4.

610 ЧАСТЬ IV Мощные средства и методы отладки неуправляемого кода
Странно, но похоже, что многие C/C++ программисты не подозревают о су ществовании этой библиотеки. Ее таинственность объясняется тем, что по умол чанию многие ее свойства отключены. Однако, как только вы установите соот ветствующие флаги, вы сразу поймете, сколь богатые возможности от вас усколь зали. В этой главе я сначала представлю библиотеку DCRT и опишу два ее расши рения, MemDumperValidator и MemStress, которые предоставят вам еще более раз витые возможности. Кратко рассмотрев DCRT, я расскажу о разных аспектах от ладки памяти, такие как кучи ОС, отслеживание записи по случайным адресам и использование бесплатных, но крайне полезных инструментов от Microsoft: Page Heap и Application Verifier. Наконец, как я и обещал в главе 2, я опишу удивитель ные ключи проверки ошибок в период выполнения (/RTCx) и ключ безопасности (/GS), повышающие эффективность отладки памяти и безопасность программ, написанных на Visual C++, на недостижимый раньше уровень.
Особенности стандартной отладочной библиотеки C
Главное достоинство библиотеки DCRT — удивительные возможности слежения за памятью куч. Она позволяет следить за всей памятью, выделяемой в отладоч ных компоновках при помощи стандартных функций C/C++, таких как new, malloc
иcalloc, а также за записью данных до начала выделенного блока памяти (under write) и после его окончания (overwrite). Обо всех этих ошибках сообщает сама DCRT посредством утверждений (assertion). DCRT также следит за утечками памя ти, сообщая о них при завершении программы посредством функции Output7 DebugString, вывод которой появляется в окне Output отладчика. Если вы работа ли над приложениями, использующими библиотеку Microsoft Foundation Class (MFC), то сталкивались при завершении своих программ с отчетами об утечке памяти; их посылала вам библиотека DCRT. MFC подключает некоторые ее функ ции автоматически.
Другое полезное свойство библиотеки DCRT — ее подсистема сообщений (мы с вами назвали бы ее трассировщиком), обеспечиваемая макросами _RPTn и RPTFn
иутверждениями. О поддержке библиотекой DCRT утверждений и их использо вании я писал в главе 3. Как я говорил, утверждения DCRT очень полезны, но они уничтожают значение последней ошибки, что может приводить к различному поведению отладочных и заключительных компоновок. Я советую применять для своих утверждений макрос SUPERASSERT, код которого включен в BUGSLAYERUTIL.DLL.
Еще одна приятная особенность библиотеки DCRT в том, что ее исходный код поставляется вместе с компилятором. Список всех ее файлов см. в табл. 17 1. Если при установке Microsoft Visual Studio .NET вы установили исходный код библио теки CRT, что я очень рекомендую, то сможете найти весь исходный код библио тек CRT и DCRT в подкаталоге <каталог установки Visual Studio .NET >\VC7\CRT\SRC.

ГЛАВА 17 |
Стандартная отладочная библиотека C и управление памятью |
611 |
|
|
|
Табл. 17-1. Исходные файлы стандартной отладочной библиотеки C |
|
|
|
|
|
Исходный файл |
Описание |
|
DBGDEL.CPP |
Определение глобального отладочного оператора delete. |
|
DBGHEAP.C |
Определения всех отладочных функций работы с кучами. |
|
DBGHOOK.C |
Заглушка функции ловушки выделения памяти. |
|
DBGINT.H |
Объявления внутренних данных и функций отладочной библиотеки. |
|
DBGNEW.CPP |
Определение глобального отладочного оператора new. |
|
DBGRPT.C |
Определения отладочных функций подсистемы сообщений. |
|
CRTDBG.H |
Заголовочный файл, который вы будете включать в свои програм |
|
|
мы. Он находится в стандартном каталоге включаемых файлов. |
|
|
|
|
Стандартный вопрос отладки
Зачем мне стандартная отладочная библиотека C, если я использую средство обнаружения ошибок наподобие BoundsChecker?
Такие инструменты обнаружения ошибок, как BoundsChecker компании Compuware или Purify от Rational Software автоматически обрабатывают запись данных до начала и после окончания выделенной памяти, а также ее утечки. Если вы работаете с одним из этих средств, вам может казаться, что использование библиотеки DCRT не стоит затрат времени и усилий. С технической точки зрения это верно, однако, чтобы гарантировать на хождение всех проблем с памятью, отладочную компоновку приложения нужно всегда выполнять под управлением средства обнаружения ошибок. Это должны делать не только вы и ваши коллеги по группе, но и, если вы следовали моим советам, приведенным в главе 2, даже сотрудники отдела кон троля качества. Не думаю, чтобы все люди были такими ответственными.
Библиотека DCRT подобна хорошей страховке от пожара или кражи. Все мы надеемся, что такая страховка нам не понадобится, но порой она мо жет спасти нас от разорения. Не упускайте ни одной возможности прове рить данные своей программы. Библиотека DCRT не вызывает значитель ного снижения быстродействия программ и в то же время может указать на некоторые очень коварные ошибки. Вам следует использовать ее всегда, даже если вы применяете все средства обнаружения ошибок в мире.
Использование стандартной отладочной библиотеки C
Чтобы вы начали как можно раньше извлекать выгоду из слежения за памятью, библиотеку DCRT нужно прежде всего подключить. Для этого в главный преком пилированный заголовочный файл (или любой другой заголовочный файл, вклю чаемый во все исходные файлы проекта) надо добавить такую строку, указав ее перед всеми директивами #include:
#define _CRTDBG_MAP_ALLOC

612 ЧАСТЬ IV Мощные средства и методы отладки неуправляемого кода
После всех остальных заголовочных файлов нужно включить файл CRTDBG.H. Благодаря определению _CRTDBG_MAP_ALLOC вызовы обычных функций выделения и освобождения памяти будут перенаправляться их специальным версиям, записы вающим при каждой такой операции сведения об исходном файле и номере строки.
После этого нужно включить средства проверки кучи, обеспечиваемые библио текой DCRT. Как я уже упоминал, большинство из них по умолчанию отключено. В документации утверждается, что они отключены для уменьшения объема кода и повышения быстродействия программы. Конечно, для заключительных компо новок это очень важно, но не забывайте, что назначение отладочных компоно вок как раз в нахождении ошибок! Увеличение объема и снижение быстродей ствия отладочных компоновок не играют большой роли. Поэтому без колебаний включайте все средства библиотеки DCRT, которые, по вашему мнению, могут пригодиться. Для их включения нужно передать функции _CrtSetDbgFlag набор флагов, объединенных операцией ИЛИ (табл. 17 2).
Табл. 17-2. Флаги стандартной отладочной библиотеки C
Флаг |
Описание |
_CRTDBG_ALLOC_MEM_DF Подключает механизмы отладочного выделения памяти и использование идентификаторов блоков памяти. Это един ственный флаг, установленный по умолчанию.
_CRTDBG_CHECK_ALWAYS_DF Выполняет проверку всей памяти при каждом запросе об ее выделении и освобождении. Установка этого флага позволя ет обнаруживать запись данных вне блока памяти как мож но раньше после возникновения ошибки.
_CRTDBG_CHECK_CRT_DF После установки этого флага во всех операциях обнаруже ния утечек и изменений блоков памяти проверяются блоки _CRT_BLOCK. Как правило, устанавливать его следует только при проблемах с функциями библиотеки CRT. При этом вы будете получать сообщения о выделении памяти библиоте кой CRT. Так как она должна иметь выделенную память вплоть до истинного завершения вашей программы, что происходит после вывода сообщений об утечках памяти, то после установки этого флага вы увидите массу ложных сообщений об утечках.
_CRTDBG_DELAY_FREE_MEM_DF После установки этого флага действительное освобождение памяти не выполняется. Блоки продолжают храниться во внутреннем списке кучи, но заполняются значениями 0xDD, благодаря чему вы легко можете узнать освобожденную па мять, изучая ее в отладчике. Этот флаг позволяет вам прове рить свою программу в условиях нехватки памяти. Кроме того, библиотека DCRT следит за тем, чтобы все ячейки освобожденных блоков памяти оставались равными 0xDD, что поможет вам обнаружить попытки повторного доступа к ним. Устанавливайте этот флаг всегда, но помните, что требования вашей программы к памяти при этом легко могут удвоиться, потому что освобожденная память не возвращается в кучу.
_CRTDBG_LEAK_CHECK_DF Проверяет утечки памяти в конце программы. Установка этого флага просто обязательна.