Как создавать диалоговые окна с помощью VBScript? Всем привет, в этом уроке я вам покажу, как быстро создавать диалоговые окна в Windows, с помощью которых мы так часто подшучиваем над своими коллегами. Я представлю небольшой обзор команд с пояснениями для того, чтобы вы не просто копировали диалоговые окна с сайта, а смогли сами импровизировать, одновременно обучаясь. Это будет знакомый вам по предыдущим компьютерным приколам скриптовый язык VBScript, и всё, что нам как всегда понадобится, это простой Блокнот, который мы, после заполнения кода, будем сохранять в укромном месте с расширением .vbs.
VBScript — это простой язык программирования, исполняемый в системе сценарием Windows Script Host. Легко кодируется в Блокноте, а лучше в специальных текстовых редакторах с расширенными возможностями, как например, AkelPad или Notepad ++ (они, в отличие от встроенного Блокнота, понимают русскую кодировку; выберите Кириллицу — Windows-1251). Именно эта связка сценария и скрипта используется Windows для того, чтобы создавать диалоговые окна . Это гораздо проще, чем использовать такие языки как С и С ++. Однако подробное знакомство с VBScript задачей настоящей статьи не ставилось; здесь я лишь приведу ряд конкретных примеров того как создавать диалоговые окна с более-менее осмысленным для читающего содержанием и вариантами развития событий по результатам действий.
Как создавать диалоговые окна с ошибкой?
Итак, создадим простое диалоговое окно с двумя кнопками. И создадим окно с псевдоошибкой. Откроем текстовый редактор, и создадим сообщение, в котором:
x=msgbox(«содержание_ошибки», 5+16, «автор_ошибки»)
Кавычки и скобки обязательны. Значок «+» позволяет использовать в одном скрипте множественные функции окна. В поле «содержание ошибки» вы можете написать любое сообщение, которое впоследствии прочитает пользователь. А в поле «автор ошибки» напишите того, кто это сообщение якобы пришлёт. Ими могут быть сама система Windows, или сосед по офису. А может быть директор или администратор. Решать опять же вам. Останавливаться не буду, идём дальше. Вот каким будут выглядеть диалоговые окна после того, как вы сохраните документ в формате .vbs.
Поле «5+16» приказывает системе создавать диалоговые окна с ярлыком критической ошибки и двумя кнопками. Это, как видно, кнопки «повтор» и «отмена». Но как вы уже поняли, можно создавать диалоговые окна и с другими настройкам, и всё это благодаря комбинации цифр или чисел. Это — числовые выражения постоянных, отвечающие за выводимые кнопки, значки и режимы окна. Подробнее смотрим ниже
0 (кнопка ОК)
1 (кнопки OK and Отмена)
2 (кнопки Прервать, Повтор и Пропустить)
3 (кнопки Да, Нет, Отмена)
4 (кнопки Да и Нет)
5 (кнопки Заново и Отменить)
16 (значок критической ошибки)
32 (значок помощи)
48 (значок предупреждения)
64 (значок информации)
256 (вторая кнопка по умолчанию)
512 (третья кнопка по умолчанию)
768 (четвёртая кнопка по умолчанию)
4096 (окно будет постоянно мешаться, перекрывая остальные окна других программ, пока пользователь не отреагирует)
262144 (поверх других окон)
524288 (текст сообщения выводится от правой части окна)
Нажатие кнопок для указанного кода, и, конечно, числовых комбинаций ни к чему не приведёт, но вы в силах будете указать системе, сколько раз те или иные сообщения будут появляться. Для этого просмотрите статью Множественные сообщения об ошибке. Если понадобится перенести строки в диалоговых окнах, можно воспользоваться написанием скрипта в формате:
wscript.quit MsgBox («Привет!»&vbNewLine&»Как сам-то?»,0)
Множество постоянных и переменных.
В простейшем написании скрипта именно знак «+» позволит применять сразу несколько параметров в одном скрипте. Например, для написания окна типа:
Просто повторите скрипт так:
MsgBox «Немедленно выключить компьютер», 0 + 48 + 4096 + 524288, «Администратор»
Кстати, появление окна можно запустить по кругу, указав в скрипте команду на запуск его же по нажатию на любую из кнопок:
В прошлый раз я говорил про взаимодействие плагинов между собой и то, какие возможности дают эти техники для прочих сценариев, без общения плагинов. В этой части я хотел бы рассмотреть обработку ошибок и отладку в плагинах. Я выношу эту тему вперёд других частей, потому что наша система становится всё более сложной и вам лучше бы уметь решать в ней проблемы, прежде чем усложнять её далее.
См. также Разработка API (контракта) для своей DLL и Разработка системы плагинов, часть 9: подводные камни.
Оглавление
- Основы отладки:
Запуск отладки плагиновОстановка выполнения:Инструменты отладчика:
Практический пример
- Запуск отладки плагинов
- Остановка выполнения:
- Инструменты отладчика:
- Практический пример
- Управлением временем жизни
- EAccessViolation
- Обработка ошибок в плагинах:
SafecallСтандартные средстваДополнение стандартных средств - Safecall
- Стандартные средства
- Дополнение стандартных средств
- Заключение
Основы отладки
Сперва мне хотелось бы дать краткое введение в отладку. Если вы уже знакомы с этим материалом, то можете пропустить его. Для тех же, кому краткого введения окажется мало — см. полную версию. Я привожу здесь эту вводную часть потому, что далее я буду говорить слова вроде «ставим non-breaking бряк на строчку XYZ с логгингом стека» и мне хотелось бы, чтобы читатели понимали, о чём идёт речь.
Отладчик — это один из основных инструментов любого программиста. Он является составной частью среды Delphi и предназначен для поиска ошибок в программе. Отладчик позволяет выполнять пошаговую трассировку (выполнение кода по шагам), просматривать значения переменных в процессе выполнения программы, устанавливать точки останова (breakpoint) и т.д. Отладка — это процесс работы с программой в отладчике, при котором обнаруживают, локализуют и устраняют ошибки.
Все основные команды, через которые Delphi переходит в режим отладки, находятся в меню «Run»:
На самом деле, всякий раз, когда вы запускаете программу из среды Delphi по F9 или командой меню «Run»/»Run», вы запускаете программу под отладчиком. Для простого запуска программы (вне отладки) есть команда «Run without debugging» (Ctrl+Shift+F9).
Примечание: команда «Run without debugging» есть не во всех версиях Delphi. А в самых последних версиях Delphi обе команды вынесены на панель инструментов:
Примечание: команда «Run without debugging» эквивалентна компиляции программы и ручному запуску её с диска вне среды. В меню она вынесена просто для удобства, чтобы не нужно было искать exe-файл в файловой системе. Если вы хотите посмотреть, как поведёт себя программа без опёки отладчика — используйте «Run without debugging».
Итак, оказывается, что в 99% случаев запуска программы — вы запускаете программу именно под отладчиком.
Запуск отладки библиотек (плагинов)
Замечу, что запускать на выполнение можно только программы. Библиотеки (DLL или bpl) — это не программы. Это именно что библиотеки — наборы функций. Их нельзя запустить. Но их можно загрузить и вызвать функцию из них. Загружать библиотеку — должен «кто-то», а именно — какая-то программа. Вот её, эту программу, вам и нужно запускать, когда вы хотите отладить библиотеку. Программа запустится, загрузит библиотеку и будет вызывать её функции.
Поскольку все плагины являются у нас библиотеками, то при попытке «запустить» плагин вы получите такое сообщение:
Если вы хотите скомпилировать плагин (получить .dll или .bpl файл) — вам вовсе не нужно его для этого запускать. Просто выберите Project/Compile (Ctrl+F9) или Project/Build (Shift+F9).
Если же вы хотите именно отлаживать плагин, то вам нужно указать программу, которая будет грузить этот плагин. Естественно, в случае плагинов это будет программа-ядро. Для этого вам нужно открыть меню Run/Parameters и указать главную программу:
Остановка выполнения программы
Пока программа работает, вы не много можете с ней сделать. Для того чтобы воспользоваться отладчиком, вам нужно приостановить её выполнение. У вас на выбор есть три варианта, первый — нажать на кнопку паузы («Run»/»Program pause»), второй — возбудить в программе исключение (или же оно возникнет в программе само — например, EAccessViolation), третий — расставить в нужных местах точки останова (breakpoint-ы, брейкпойнты или просто «бряки»).
Пауза
Способ первый не позволяет достичь точности. Вы останавливаете программу в тот момент, когда вы нажимаете на кнопку. Это может быть за миллион строк кода до или после того места, где вы в действительности хотели бы быть. Поэтому этот способ используется, когда вам не важно точное место останова. Пример — зависшая программа. Вы просто останавливаете её выполнение в любой момент, чтобы посмотреть, что же там произошло, что программа зависла. Ещё вариант — вам нужна программа на паузе, чтобы проанализировать, скажем, глобальные переменные. Вам не важно место останова, потому что вас интересуют значения переменных, которые, будучи раз заданными, не меняются во время работы программы.
Исключения
Второй способ заключается в том, что в отладчике Delphi есть полезнейшая возможность раннего уведомления об исключениях. Каждый раз, когда в программе возникает исключение, отладчик отображает такое окно:
Формат сообщения всегда одинаков: «Project XXX raised exception class YYY with message ZZZ». Где XXX — имя процесса (проекта), где возникло исключение, YYY — имя класса исключения и ZZZ — сообщение об ошибке в объекте исключения.
Это окно возникает прямо в момент возбуждения исключения до того, как получит управление хоть один блок обработки исключения. Остановка программы в момент возникновения исключения позволяет вам немедленно исследовать ситуацию, используя инструменты отладчика (рассмотрим их чуть ниже) и либо исправить проблему, либо продолжить выполнение программы.
Заметим, что окно это появляется только при отладке. Его появление во время запуска программы из-под Delphi ещё не говорит о том, что при запуске программы вне среды появится хоть какое-то сообщение. Нажав на «Continue» (только в новых Delphi), вы продолжите выполнение программы (с первого блока обработки исключения), а нажав на «Break»/»Ok», вы перейдёте в отладчик, где сможете исследовать ситуацию возникновения исключения.
Иными словами, если вы видите такое окно — это значит, что в вашей программе произошло событие «возбуждение исключения». Окно показывается до выполнения кода обработки, поэтому его показ ещё не означает, что ваша программа вообще покажет хоть какое-то сообщение об ошибке. Хорошим примером является наша функция мульти-загрузки плагинов из папки из первой части: там мы ловили ошибки загрузки плагинов, собирая информацию, но не показывая сообщений об ошибках, а в конце загрузки возбуждали единственное исключение.
Если вы хотите посмотреть, как программа будет работать «вживую», без отладчика и его уведомлений — просто запустите программу вне отладчика (через «Run without debugging» или просто запустив программу руками с диска).
Иногда в этом окне также появляется опция «Show CPU view»:
Она показывается только в том случае, если место возникновения исключения не соответствует строчке исходного кода. Если галочка будет установлена, то после нажатия на «Break» откроется окно CPU-отладчика точно спозиционированное на место возникновения ошибки, иначе (галочка сброшена) — редактор исходного кода с ближайшим местом по стеку вызова (если отладчик вообще сумеет что-то найти).
Заметим, что опция «Show CPU view» показывается достаточно редко. Кстати говоря, её отсутствие в окне уведомления отладчика ещё не говорит о том, что при нажатии на «Break» вы не увидите CPU-отладчика. Более подробно об этом я скажу чуть ниже.
Примечание: кроме уведомления об исключениях, отладчик может показывать и иные сообщения. Их не следует путать с уведомлениями об исключениях.
Точки останова
Третий способ является основным. Заключается он в том, что по тексту программы вы мышкой отмечаете места, где вы хотели бы остановиться во время работы программы. Слева от кода вы видите полоску, в которой появляются синие точки:
Каждая синяя точка говорит о том, что в реальном машинном коде программы есть код, который попал туда именно в процессе компиляции этой строки. Т.е. строка, отмеченная синей точкой, компилируется в код, приводит к появлению кода. Если напротив строки с кодом вы не видите точки, то:
Итак, после того, как вы сделали Build проекту и видите синие точки — теперь вы можете выбрать место для установки точки останова. Щёлкните мышкой по любой синей точке, и она изменится на большую красную точку, а сама строка выделяется (кстати, если вы не видите синих точек вообще, необязательно делать компиляцию, чтобы их увидеть — просто щёлкайте слева от кода, где вы хотели бы остановиться):
В этом случае мы захотели остановиться перед выполнением строки с присваиванием свойства Caption. Заметим, что breakpoint-ы вы можете ставить, как во время проектирования, так и во время работы или приостановки программы. Теперь, после запуска программы, как только выполнение дойдёт до одной из заданных вами точек останова, отладчик немедленно остановит программу.
Инструменты отладчика
Итак, после остановки программы в отладчике (любым из трёх описанных выше способов) вы можете использовать его возможности для анализа программы. Большинство вещей, о которых мы будем сейчас говорить, доступны именно в режиме отладки (например, в контекстное меню редактора кода в режиме отладки добавляются новые команды). Вы можете определить, в каком режиме находится среда, взглянув на заголовок окна:
Для примера возьмите любую свою программу, поставьте breakpoint на первое действие при нажатии какой-нибудь кнопки, запустите программу и щёлкните по кнопке (мы сейчас будем обсуждать возможности отладчика, а вы сможете щупать их прямо на своей программе). Если вы используете новые Delphi, то заметите, как преображается при этом среда — исчезает инспектор объектов, палитра компонентов и т.п. Зато появляется множество окон: «Call Stack», «Watch List», «Local Variables» и т.п. Каждое из этих окон предоставляет вам какую-то возможность отладчика:
Если какого-то окна на экране нет, вы можете показать его, используя меню «View»/»Debug windows»:
Если вы не видите на экране какое-то окно, о котором идёт речь, — просто выберите его из этого меню.
Попробуйте сейчас, пока программа стоит на паузе, переключиться на программу. Вы щёлкаете по кнопке в панели задач, но ничего не происходит. Попробуйте свернуть все окна, вы увидите примерно такую картину:
Вы можете видеть, что наша программа как бы висит (последним сворачивалось окно Delphi, поэтому рисунок окна Delphi отпечатался на окне нашей программы). Она не прорисовывается, она не реагирует на ваши действия, другим словом — висит. Да, но не забывайте, что мы только что поставили с вами программу на паузу! Это значит, что она не работает. А если программа не работает, то она и не может ни перерисовываться, ни реагировать на ваши щелчки мышью. Так что ничего страшного в таком поведении нет — так и должно быть. Как только вы возобновите работу программы (снимите её с паузы), она снова будет вести себя как полагается.
Анализ значений переменных
Итак, продолжаем. Наша программа стоит на паузе. В окне «Local Variables» показываются локальные переменные в текущем месте. Как только мы остановились, отладчик показывает нам чему равны локальные переменные в текущей функции (т.е. функции, в которой мы остановились). Если вы хотите посмотреть значения локальных переменных для других функций — просто дважды щёлкните по нужной функции в окне стека вызова (Call Stack).
Для некоторых переменных отладчик может сказать нам, что он не может получить значение переменной («Variable ‘XYZ’ inaccessible here due to optimization»). Это работа оптимизатора (кстати, вы можете отключить его, сбросив в опциях проекта галочку «Optimization»). Он выбрасывает переменную, как только в ней отпадёт необходимость.
Итак, «Local Variables» — удобное окно для просмотра локальных переменных. Что делать, если хочется посмотреть не локальную переменную? Можно воспользоваться окном «Watches». Для этого щёлкните правой кнопкой по свободной области окна «Watch List» и выберите «Add watch» — появится окно ввода параметров наблюдения:
В поле «Expression» вы можете ввести имя переменной, за которой хотите следить. Кстати, это не обязательно должна быть переменная — вы можете ввести любое выражение, которое поддаётся вычислению. Например, выражение «X = 1» (без кавычек, разумеется) — оно будет равно ‘True’ или ‘False’. Остальные опции отвечают за форматирование отображения. Другой способ добавить выражения для слежки — выделить их в редакторе кода, щёлкнуть правой кнопкой и выбрать «Add watch at cursor» (Ctrl + F5).
Примечание: обычно команды отладчика располагаются в подменю «Debug» (и многие из них могут быть недоступны, если только программа на стоит на паузе под отладчиком), но если в настройках отладчика включить опцию «Rearrange editor local menu on run», то на время отладки все пункты контекстного меню редактора, связанные с отладкой, для удобства выносятся наверх.
Вот пример окна «Watches» после добавления нескольких переменных и выражений для наблюдения:
Последние два выражения с X демонстрируют два различных вида представления одной и той же величины. В первом случае мы не меняли способ отображения, а во втором — установили значение в «Memory Dump». Это может быть полезно, если умалчиваемый вид не даёт достаточной информации — см., например, вопрос №65263. Заметим, что выражение «IntToStr(Tag)» не может быть вычислено («Inaccessible value»), т.к. для того, чтобы посмотреть значение этого выражения, нужно вызвать функцию (а именно — функцию IntToStr). Вызов функции не является безопасным действием, т.к. может иметь побочные эффекты. Например, процедура может менять значение глобальной переменной или даже показывать сообщения. Но если вы уверены, что введённое вами значение вычислять безопасно, вы можете зайти в свойства watch-а и установить галочку «Allow function calls». После этого отладчик сможет показать значение выражения «IntToStr(Tag)», а именно — ‘1’ (строка, а не число). Но будьте аккуратны!
В поле «Expression» вы видите выражение, которое вы выделяли в редакторе кода (в нашем случае мы просто поставили курсор на слово «Tag»). В поле «Result» показывается текущее значение выражения. Вы можете изменять выражение и нажимать кнопку «Evaluate» для вычисления введённого значения. Также вы можете задать новое значение в поле «New value» и нажать кнопку «Modify». Разумеется, возможность модификации доступна не всегда. Например, вы не можете модифицировать выражение «Tag = 1», равное True, на значение False. Вместо этого вы должны модифицировать значение самого Tag — одной из переменных, участвующих в выражении.
Примечание: кстати говоря, не следует думать, что модификация переменной в любом окне отладчика — это очень простая операция, заключающаяся в изменении памяти, занимаемой переменной. Это может быть и верно для простых типов типа Integer, но не для сложных динамических типов типа String и массивов. Дело в том, что для них ведь нужно выделить память, а старое значение нужно удалить. Поэтому изменение таких переменных ведёт к вызову функций менеджера памяти программы — несмотря на то, что при этом вся пограмма находится на паузе! В типичных ситуациях это не имеет значения, но в некоторых из-за таких побочных эффектов может получаться самое различное поведение программы. Просто имейте этот момент в виду.
Альтернативным способом для быстрого просмотра значений переменных и выражений является использование всплывающих подсказок — достаточно подвести курсор мыши к имени переменной в редакторе кода (либо выделить выражение и навести на него мышь) и через короткое время всплывёт подсказка со значением переменной (в случае, если выражение можно вычислить):
Хотя если подсказка не всплывает — это ещё не значит, что интересующее вас выражение нельзя вычислить. Возможно, среда просто не понимает, чего вы хотите 🙂 Попробуйте посмотреть выражение через «Evaluate/Modify».
Анализ пути выполнения
Следующее окно, которое мы рассмотрим — это «Call Stack». Так называемый стек вызовов:
Это окно показывает, какие процедуры вызывались до того, как выполнение дошло до текущего момента (текущего — т.е. там, где мы встали на паузу). Читать его нужно снизу вверх (текущий момент находится сверху, а начало программы — в самом низу). Например, на скриншоте выше мы видим, что процедура A вызывалась из P, которая в свою очередь вызвалась из Button2Click (мы смотрим сверху вниз, т.е. в обратном направлении). Также это окно пытается показывать аргументы вызова. Но для этого они должны быть доступны. Помните, что мы говорили про оптимизатор в обсуждении окна «Local Variables»? Те же слова применимы и здесь.
Текущая процедура (т.е. та, в которой мы находимся) в этом окне маркируется стрелочкой.
По поводу странного вида процедур до Button2Click мы ещё поговорим позже.
Это окно — очень важный инструмент при поиске источника ошибок. Например, при остановке после исключения вы ведь понятия не имеете, что происходит в программе. Взглянув на «Call Stack», вы легко определите, где вы находитесь и как вы сюда попали. Более того, вы можете дважды щёлкнуть по любой строке в этом окне — и вы автоматически попадёте в соответствующее место. Например, если вы сейчас щёлкните по строке с «Unit9.P» в окне «Call Stack», то вы мало того, что перейдёте в редакторе кода к процедуре P, так ещё и строка вызова процедуры A будет подсвечена красным цветом. Очень удобно, если одна процедура вызыватся несколько раз в разных местах. Щёлкнув по нужной строке в этом окне, мы легко определим, откуда был сделан вызов.
Трассировка
Итак, с помощью рассмотренной функциональности вы можете анализировать любую ситуацию в программе — проверять, чему равны у вас переменные, даже вычислять выражения, следить за путём выполнения программы. Но это только одна статичная ситуация из множества возможных. Мы пока всё ещё стоим на месте. Но отладчик позволяет нам больше, а именно: он позволяет выполнять программу по шагам, по строчкам. Посмотрите на последний снимок экрана: мы встали на заданной точке останова. Точка останова показана красной точкой слева от строки кода. Но вы также можете видеть поверх неё небольшую голубую стрелочку, которой не было, когда мы устанавливали точку останова в режиме проектирования. Эта стрелочка показывает, что сейчас будет выполнена указанная строка. Для выполнения есть две основные команды — «Step over» (F8) и «Trace into» (F7). Нажмите, например, на F8. Вы увидите, как стрелочка переместится к следующей строке:
Вы видите, что программа снова стоит на паузе. Мы только что выполнили строчку с ShowMessage.
Таким образом, мы с вами можем выполнять по шагам любой блок кода. Если вы не можете понять, почему ваша программа ведёт себя так, а не иначе — просто поставьте бряк на свой код, и пройдитесь по коду после остановки, выполняя каждую строчку и смотря, как и куда идёт выполнение кода, какие значения каким переменным назначаются и т.п. Большие блоки кода вы можете пропускать, ставя новые бряки и используя команду «Run»/»Run» (F9) или устанавливая курсор в нужную строку и используя «Run to cursor» (F4).
Напомним, что у нас есть две команды для пошагового выполнения — «Step Over» (F8) и «Trace Into» (F7). С первой мы уже познакомились — она просто выполняет текущую строчку и переходит на следующую. «Trace Into» работает похожим образом, но с одним отличием: если в текущей строчке есть вызов процедуры, то «Trace Into» зайдёт внутрь процедуры, в то время как «Step Over» выполнит всю процедуру одним махом. Если никаких вызовов процедур нет, то эти команды ведут себя одинаково.
Например, положим, что мы установили точку останова на вызов некоторой функции — скажем, P. Тогда, если бы вы стояли на «P;» и нажали бы F8, то программа выполнила бы P целиком, после чего мы бы оказались в отладчике на строке после «P;». А если бы нажали на F7, то мы перешли бы в процедуру P, оказавшись на сроке «begin». Разумеется, если бы мы ещё поставили точку останова внутри P, то при попытке выполнить строчку с «P;» «одним махом» с помощью F8, мы всё равно оказались бы внутри P — но уже не в результате «захода в функцию», а как результат срабатывания точки останова. Это полностью соответствует описанной логике. С одной стороны, F8 выполняет строчку целиком. С другой стороны, любой бряк приводит к остановке выполнения программы. Поэтому, когда F8 выполняет строку и в процессе этого выполнения натыкается на бряк, то она останавливает выполнение программы.
Обычно при отладке вы используете F8, выполняя код строго по строчкам. Вас обычно не интересуют детали выполнения вызываемых подпрограмм, а важен лишь конечный результат. Некоторые из этих процедур могут быть весьма нетривиальными — например, запрос реквизитов пользователя и подключение к серверу могут выполняться одной процедурой, которую мы можем выполнить одним нажатием на F8. С другой стороны, мы можем быть заинтересованы в отслеживании выполнения своих собственных процедур, поэтому, когда из одной нашей процедуры вызывается другая наша процедура, то мы будем использовать F7, чтобы зайти внутрь второй нашей процедуры и проследить её выполнение. Но опять же, если мы заранее знаем, чем кончится дело, то мы не обязаны шагать по шагам по всем процедурам — мы вполне можем использовать и F8.
Кроме этих команд ещё есть ещё несколько полезных команд движения по программе — но я оставлю вам их для самостоятельного изучения.
Мы рассмотрели большинство основных возможностей для отладки программы. Два главных инструмента отладчика — это наблюдение за переменными и пошаговое выполнение. Если вы используете описанный инструментарий несколько раз, то у вас появится потребность завершить выполнение программы раньше положенного. Например, вы запустили программу, стали её отлаживать и нашли причину ошибки. Теперь вам нужно её исправить. Но ваша программа сейчас работает или стоит на паузе. Прежде, чем вернуться к редактированию текста, вы должны завершить её. Что вы будете делать? Снимать все бряки, возобновлять выполнение программы и выходить из неё? Есть способ проще — вы можете использовать «Program reset» (Ctrl + F2). Эта команда немедленно обрывает выполнение программы. Её можно рассматривать как аналог команды «Завершить процесс» в Диспетчере Задач, только чуть более гуманный по отношению к среде Delphi.
Примечание: никогда не используйте обычное снятие процесса отлаживаемой программы. Отладчик крайне болезненно относится к снятию процесса извне. Всегда используйте только «Program reset».
Далее, вспомните про понятие отладочной информации, о котором мы говорили выше. Если модуль был скомпилирован без отладочной информации, то использовать обычный отладчик для него вы не сможете. Т.е. не будут работать бряки, поставленные на код этого модуля. Вы не сможете зайти по F7 в любую процедуру этого модуля и т.п. Посмотрите хотя бы на снимки экрана чуть выше: у нас есть вызовы ShowMessage и IntToStr. Если вы попробуете в них зайти, то ничего не выйдет — F7 сработает как обычная F8. Это как раз и происходит потому, что нет отладочной информации для модулей Dialogs и SysUtils соответственно. Все стандартные модуля Delphi не имеют отладочной информации. Обычно это очень удобно — ведь вам большую часть времени не нужно отлаживаться внутри стандартных процедур. Однако если вам всё же нужно это сделать (например, по непонятным причинам вылетает Assign для стандартного TTreeView и вы должны выяснить почему), то вы можете переключиться между обычной и отладочной версией системных модулей. Для этого вы устанавливаете галочку «Use debug DCUs». После этого вы можете использовать F7, чтобы заходить в стандартные процедуры, в частности, вы теперь можете зайти и в IntToStr. Разумеется, эта опция работает только для стандартных модулей Delphi. Для того чтобы использовать отладочную версию своих модулей — вы должны перекомпилировать их с нужными опциями.
Примечание: есть тут ещё один тонкий момент. Если вы компилируете своё приложение с пакетами времени выполнения, то модули, для которых вы хотите включить/выключить отладку могут находиться в пакете, а не в программе. И на них эти опции влиять, разумеется, не будут. Возможно, вам придётся пересобрать свои пакеты или временно отключить компиляцию с пакетами.
Посмотрите на наш пример, когда мы говорили про окно «Call Stack». Мы заметили, что все процедуры ниже Button2Click имеют странный вид. Это как раз и происходило потому, что все эти процедуры являлись стандартными процедурами Delphi и поэтому размещались в модулях без отладочной информации. Если бы мы включили опцию «Use debug DCUs», то наш стек вызовов выглядел бы так:
Как видим, «странными» у нас остались только функции из системных библиотек — для них, очевидно, отладочной информации у нас нет.
События
Следующее окно для ознакомления — «Event Log»:
Это вид окна при запуске процесса. А вот его вид после некоторой работы:
По-умолчанию, лог очищается при каждом запуске процесса. Вы также можете сохранить его в файл для анализа или очистить руками в середине работы — для этого воспользуйтесь соответствующими командами из контекстного меню. Кроме того, в опциях отладчика есть настройка окна «Event Log» (которая также доступна из контекстного меню окна «Event Log»).
В частности, помимо настройки поведения и внешнего вида, здесь можно включить/отключить логгинг определённых типов событий. Если интересующее вас событие происходит редко и/или тонет в общей массе событий, можно просто выключить все другие типы событий. Именно это является причиной, почему по-умолчанию отключен логгинг сообщений Windows — их всегда бывает очень много. Кроме того, вероятно, вы захотите отключить опцию «Display process info with events» — она показывает дополнительную информацию о процессе, вызвавшем событие. Поскольку чаще всего вы будете отлаживать только один процесс, эта информация не несёт полезной нагрузки и только создаёт шум в логе. В случае отладки двух процессов эта опция позволит отличать события от разных процессов.
Расширенные точки останова
В самом начале этого пункта мы буквально краем коснулись точек останова с целью быстрее познакомить вас с возможностями отладчика, т.к. они (возможности) доступны только в режиме остановки программы, а точки останова являются основным средством для установки программы на паузу. Теперь мы рассмотрим их более подробно. И для этого сначала взглянем на окно «Breakpoints»:
Это окно содержит список всех точек останова в вашем проекте. Отсюда вы можете управлять ими всеми. Можно, например, удалить все точки останова, когда вы закончили отладку. Можно добавлять точки останова. Можно редактировать их свойства и временно отключать (disable). Точка останова не активна (т.е. не работает), если галочка слева от неё сброшена. Удобно временно отключать точку останова, если сейчас она вам только мешается, но в будущем ещё понадобится. Тогда вы сейчас её отключаете, а когда она снова понадобится — включаете (enable) обратно.
Кстати, включить/выключить точку останова, а также открыть окно её свойств вы можете, щёлкнув правой кнопкой мыши по красному кружку точки останова в левой части редактора кода:
Взглянем теперь на свойства точки останова (заметим, что некоторые их этих свойств вы можете редактировать прямо в окне «Breakpoints», не открывая окна свойств):
Первые две строки задают место установки точки останова. Обычно они задаются автоматически, когда вы мышью ставите точку останова, но вы можете указывать их и руками — например, при ручном добавлении точки останова через команду «Add breakpoint». Строка «Condition» задаёт дополнительное условие. Если она пуста (по-умолчанию) — бряк срабатывает каждый раз, когда до него доходит выполнение, если она не пуста (задана), то он срабатывает только в случае, если условие в данном поле истинно. Разумеется, то, что вы сюда впишете, должно вычисляться, когда выполнение доходит до точки останова, и, кроме того, всё выражение в целом должно иметь тип Boolean.
Строка «Pass Count» определяет, на который проход мимо точки останова отладчик остановит программу. 0 или 1 означает немедленную остановку. Например, если бы мы указали «Pass Count» равным двум в нашем примере, то мы бы пропустили первую итерацию цикла и остановились бы только на второй итерации. После срабатывания точки останова счётчик сбрасывается, и отсчёт начинается снова (поэтому, мы пропустили бы третью итерацию цикла и остановились бы на четвёртой, если бы она у нас была). Может комбинироваться с полем «Condition». В этом случае сперва высчитывается поле «Condition» и, если оно равно True, то проверяется/изменяется счётчик «Pass Count».
Поле «Group» определяет группу, в которую входит точка останова. Обычно используется, если у вас много точек исключения. В этом случае их можно сгруппировать в группу и управлять всеми точками останова в группе (например, включать/выключать) одновременно как единым целым. Для включения точки останова в группу просто введите её имя в поле «Group». Если вы уже вводили название группы для другой точки останова, то вместо повторного ввода вы можете выбрать группу из раскрывающегося списка. Иногда имеет смысл включать в группу одну-единственную точку останова. Это бывает в случаях, когда вы создаёте сложные условия с помощью продвинутых (advanced) опций (описание чуть ниже).
Флажок «Keep existing breakpoint» (в старых Delphi его нет) служит для создания новой точки останова при модификации свойств уже существующей. Например, вы поставили точку останова, задали ей свойства, а потом решили поставить точно такую же точку останова, но чуть ниже, на другую строчку. Чтобы не создавать новую точку останова и не вводить все свойства заново, вы можете открыть свойства уже существующей точки останова (с проставленными свойствами), установить галочку «Keep existing breakpoint» и изменить поле «Line number» (разумеется, сначала вам нужно посмотреть в редакторе кода номер строки, на которую вы хотите установить новую точку останова).
В режиме «Advanced» (кнопка «Advanced» сворачивает или разворачивает нижнюю часть окна) вам доступны продвинутые режимы использования точек останова, которые используются значительно реже. Флажок «Break», если он установлен, определяет обычное поведение точки останова. Если вы его сбросите, то точка останова не будет приводить к остановке программы. Зачем, в таком случае, она нужна? Дело в том, что вы можете назначить некоторые события, которые будут выполняться при прохождении точки останова. Все опции в разделе «Advanced» делают именно это. Для многих из них вы, вероятно, захотите сбросить опцию «Break», т.к. вам нужно, чтобы просто сработало событие, но не нужно при этом останавливаться. В этом случае точка останова ведёт себя подобно триггеру на задаваемое действие.
Опции «Ignore subsequent exceptions» и «Handle subsequent exceptions» обычно работают парой. Если выполнение программы проходит мимо точки останова с установленной опцией «Ignore subsequent exceptions», то отладчик отключает свои уведомления об исключениях. Опция «Handle subsequent exceptions» действует ровно наоборот — она включает уведомления. Если вы отлаживаете код, в котором часто возникают исключения перед тем, как выполнение дойдёт до интересующего вас места, то вы можете установить точку останова до и после кода, возбуждающего исключения. Последовательно задавая этим точкам останова опции «Ignore subsequent exceptions» и «Handle subsequent exceptions» и сбрасывая опцию «Break», вы добьётесь игнорирования отладчиком исключений на проблемном участке кода.
Опция «Log message» заносит заданное сообщение в окно «Event Log» каждый раз, когда срабатывает точка останова.
Опция «Eval expression» вычисляет заданное выражение каждый раз при срабатывании бряка. Если при этом включена опция «Log result», то результат вычислений добавляется в «Event Log». Очень полезная функция (вместе с «Log message»), которую можно использовать для логгирования без модификации исходного кода (т.е. устанавливаются точки останова вместо OutputDebugString в коде, и логгинг работает сразу — нет необходимости перекомпилировать и перезапускать программу). Разумеется, в отличие от OutputDebugString, логгинг средствами точек останова работает только при отладке из-под отладчика Delphi и не доступен при автономном прогоне программы (для OutputDebugString при этом доступен вывод от программы DebugView). Удобно использовать эти опции для «лёгкого профайлинга» (‘лёгкого’ — в смысле примитивного): для замера времени выполнения какого-то кода, установите вокруг него две точки останова. В «Eval expression» впишите GetTickCount и сбросьте опцию «Break». После прогона разница значений в логе даст вам приближённое время выполнения участка кода в миллисекундах.
«Enable/Disable group» включает или выключает группу брейкпойнтов при срабатывании текущей точки останова. Используются довольно редко, т.к. необходимы для задания довольно сложного поведения точек останова. Один из вероятных сценариев использования этих опций — отладка двух разных потоков сразу. Например, при достижении точки останова в первом потоке отключаются все точки останова во втором потоке и наоборот. Таким образом, начав отладку одного потока (первого, в котором сработает точка останова), наш процесс отладки не прервётся другим потоком. Это избавляет вас от ручного включения и выключения точек останова при нескольких проходах отладки. Другой вариант использования этих опций — отладка системных модулей. Например, вы расставили точки останова в коде VCL. Но нужный вам код VCL выполняется при запуске приложения, а вам нужно, чтобы точки останова срабатывали только после, например, нажатия на кнопку. Поэтому можно отключить точки останова, а в нужное место (например, в dpr-файле после создания форм) поставить пустую точку останова, указав, что при проходе она должна включать все точки останова. Тогда получится, что, во время загрузки приложения точки останова будут молчать, а как только пойдёт работать ваш код — тут они и сработают.
«Log Call Stack» (нет в старых Delphi) заносит в «Event Log» стек вызовов при прохождении точки останова. Например, установив бряк с этой опцией (и без опции «Break») на начало функции, можно логгировать, кто вызывает эту функцию. Опции «Entire stack» и «Partial stack» переключают логгинг всего стека или только первых «Number of frames» записей. Это невероятно удобная опция, если у вас нет под рукой готового инструмента типа JCL или EurekaLog. Поставив точки останова с этой опцией на конструкторы класса Exception (разумеется, только с включённой опцией «Use Debug DCUs», т.к. класс Exception сидит в стандартном модуле SysUtils.pas), вы во многих случаях можете упростить отладку, т.к. при возникновении исключения в «Event Log» будет попадать стек вызова для возникшего ислючения.
Практический пример
Комплексный пример использования средств отладчика для нахождения решения проблемы можно увидеть здесь.