Access Violation
Нарушение доступа — это ночной кошмар программистов, ужас, летящий на крыльях ночи... Весь ужас в том, что очень часто это — мина с часовым механизмом, внезапно взрывающаяся после сотен, а то и тысяч строк пройденного кода.
В действительности access violation — всего лишь простая ошибка, означающая, что ваше приложение "получило по рукам" от операционной системы за попытку влезть в область памяти, ему не принадлежащей. Когда вы получаете сообщение об этом (Рисунок 2.28), вам рассказывают, кто (первое число) и куда (второе число) пытался залезть. Первое число предоставляет адрес инструкции, попытавшейся нарушить границы, а второе указывает, куда именно хотела обратиться инструкция-нарушительница.
Вернитесь в среду, выберите команду Search/Find Error, введите адрес ошибки (первое число в сообщении) в поле ввода диалогового окна, щелкните на кнопке ОК и читайте подходящую молитву. Если вам повезет, в окне появится строка, вызвавшая ошибку. Гораздо чаще этого не происходит, так как ошибка оказывается где-то в VCL или библиотеке, скомпилированной без отладочной информации, и вызывает ее передача неверного параметра в функцию, при отработке которого и происходит ошибка доступа.
Даже скомпилировав VCL с отладочной информацией, вы, скорее всего, сможете получить только имя функции, вызвавшей ошибку, и вам все равно придется потратить немало времени на поиски ошибки где-то совсем в другом месте.
Активизирование существующей копии
Сейчас, когда вы ближе познакомились с тремя типами приложений и разобрались в концепции повторного использования форм и приложений, пришла пора поговорить "о сургуче и башмаках", а точнее, о двух очень полезных компонентах— TApplication и TScreen. Они снабжают вас информацией об операционной среде выполнения программы и предоставляют возможность повысить профессионализм вашего приложения
Application
Поле Title определяет название вашего приложения, выводимого в панели задач (например, Delphi устанавливает его по имени текущего проекта). Если вы не введете названия, будет использоваться название, установленное по умолчанию (DELPHI32).
Поле Help file определяет файл справки, подключаемый к вашему приложению, что позволяет использовать систему контекстной справки.
Кнопка Load Icon позволяет установить пиктограмму приложения, используемую как пиктограмму по умолчанию в ярлыках и панели задач.
С помощью последней опции. Target file extension, можно переопределить стандартное расширение создаваемого файла (DLL — для динамически линкуемых библиотек, ОСХ — для элементов ActiveX и т.п.). Однако это расширение можно не устанавливать, так как Delphi весьма корректно работает по умолчанию.
Булевы типы
На ранней стадии обучения программисты осваивают понятие бита, два состояния которого можно использовать для записи информации о чем-либо, представляющем собой одно из двух. Бит может обозначать 0 или 1, ДА или НЕТ, ВКЛЮЧЕНО или ВЫКЛЮЧЕНО, ВЕРХ или НИЗ, СТОЯТЬ или ИДТИ. В Object Pascal информация о чем-либо, что можно представить как ИСТИНА (True) или ЛОЖЬ (False), хранится в переменных булевых типов. Всего таких типов че-тыре, и они представлены в табл. 1.4.
Целые типы
В переменных целых типов информация представляется в виде целых чисел, т.е. чисел не имеющих дробной части. Определенные в Object Pascal целые типы подразделяются на физические (фундаментальные) и логические (общие). При программировании удобнее использовать логические целые типы, которые задают объем переменных в зависимости от типа микропроцессора и операционной среды таким образом, чтобы достигалась максимальная эффективность. Физические целые типы следует применять лишь в тех случаях, когда в первую очередь важны именно диапазон значений и физический объем переменной. В Object Pascal определены следующие целые типы.
Integer
Shortint
Smallint
Longint
Byte
Word
Cardinal
Обратите внимание, что один из этих целых типов назван именно целым (integer). Это может иногда приводить к путанице, но мы легко сможем ее избежать, применяя термин целый к. группе типов, a integer — к конкретному типу, определяемому в программе этим ключевым словом. Переменные физических целых типов имеют разные диапазоны значений в зависимости от того, сколько байтов памяти они занимают (что равно значению, возвращаемому функцией SizeOf для данного типа). Диапазоны значений для всех физических типов перечислены в табл. 1.2.
Compiler
Вкладка компилятора содержит огромное количество переключателей, позволяющих устанавливать опции компилятора. Две особенно полезные опции. Show hints и Show warnings, помогут вам при отладке (при этом компилятор будет выдавать множество предупреждений, например об использовании неинициализированной переменной).
CONTECTS
Учебник по Delphi
Глава 1. Что нового в Delphi 5
Глава 2. Палитра компонентов
Глава 3. Типы данных
Простые типы данных
Строковые типы
Структурные типы
Указательные типы
Процедурные типы
Глава 4. Создание приложений
Работа с формами
Создание приложений SDI
Создание приложений MDI
Создание консольного приложения
Повторное использование приложений
Опции проекта
Передовые технологии программирования
Глава 5. Создание элементов управления ActiveX
Среда разработки Delphi ActiveX (DAX)
Библиотеки типов
Элементы управления ActiveX
Регистрация и установка элемента управления ActiveX
Создание формActiveForm
Распространение элементов управления ActiveX и форм ActiveForm в Web
Глава 6. Тестирование и отладка
Тестирование
Интегральный отладчик Delphi
Силовая отладка
Обработка ошибок
Data_type
Типы данных
С помощью типов данных программист указывает компилятору, как хранить информацию в программе. При объявлении переменной необходимо указать ее тип. Одни типы уже определены в языке, другие программисту приходится задавать самому. В ранних языках программирования допускалось ограниченное число типов данных, и Pascal оказался одним из первых языков, допускающих определение в программе новых типов.
Типы данных, определяемые пользователем, обычно задаются в разделе определения типов программы или модуля (unit), однако это можно делать и внутри процедур или функции. Объявления типов действуют в пределах того блока, в котором они размещены. Вне этого блока ссылаться на такие типы нельзя. Внутри же они заменяют все внешние типы с тем же именем. Объявленные типы данных можно применять в любом месте области их видимости; запрещена только ссылка определяемого типа на самого себя (тут, однако, есть одно исключение, касающееся указателей).
Объявления типов в Pascal являются для компилятора чем-то вроде схем, которые он должен запомнить на случай, если вдруг встретит в программе ссылки на тот или иной тип. Само по себе объявление типа не вносит в программу никаких изменений.
Что же касается объявлений var, то они задают компилятору некоторые действия, связанные с ранее объявленными типами. Тип переменной ограничивает как ее значения, так и операции, которые можно выполнять с этими значениями.
Определения типов и переменных могут размещаться в нескольких местах компонентов программы. Выглядят же они следующим образом.
type
typel = type definitioni; //Новые типы данных определяются в разделе "type". Каждому новому
// типу присваивается имя, затем он определяется через уже
//существующие типы.
type2 = type__definition2; // В одном разделе "type" можно объявить несколько типов.
//Самое простое определение типа состоит из имени типа,
type3 = typel; // определенного ранее.
// Новые переменные объявляются в
var // разделе "var". Каждой новой
var1: type definitions; // переменной сначала присваивается имя, а затем — тип (на основе
// ранее определенных типов).
var2, var3: type definition4; // В одном разделе "var" можно объявить несколько переменных.
// Нескольким переменным можно присваивать один и тот же тип.
var4 : typel; // Программу легче читать, если переменным присвоены
//существующие типы.
Синтаксис Object Pascal позволяет одновременно конструировать исключительно сложные типы и определение переменных. Однако определение типов в разделах type тех или иных блоков дает возможность использовать эти типы в разных частях программы. Новые типы определяются из типов следующих категории.
Простые типы для хранения информации в форме чисел и других "упорядоченных" значении. Строковые типы для хранения последовательностей символов. Структурные типы для одновременного хранения информации разных типов. Указательные типы для косвенного обращения к переменным заданных типов. Процедурные типы для обращения к процедурам и функциям, рассматриваемым как переменные. Вариантные типы для хранения в одной переменной данных различных типов.
Обычно идентификаторы типов используются только при определении новых типов или объявлении переменных. Есть, однако, несколько функций, в которых имя типа может использоваться как часть выполняемого оператора. Например, функция SizeOf (Т) возвращает количество байтов, занимаемых переменной Т.
Функция SizeOf очень важна для написания эффективных программ. Многие из определенных в Object Pascal типов имеют очень сложную структуру и могут занимать в памяти довольно много места. При этом элементы таких типов созданы скорее для представления значений в некотором логическом порядке, а не для того, чтобы занимать место в памяти. Функция SizeOf избавляет программиста от необходимости вычислять объем данных в подобных случаях.
Действительные типы
В переменных действительных типов содержатся числа, состоящие из целой и дробной частей. В Object Pascal определено шесть действительных типов. Все типы могут представлять число 0, однако они различаются пороговым (минимальным положительным) и максимальным значениями, которые могут представлять, а также точностью (количеством значащих цифр) и объемом. Действительные типы описываются в табл. 1.5.
Диалоговое окно Evaluate/Modif
y
Диалоговое окно Evaluate/Modify
Для вывода диалогового окна Evaluate/Modify выберите команду Run/Evaluate/Modify (рис 2.14). Другой способ вызова диалогового окна — установить курсор в окне редактирования на необходимой вам переменной, щелкнуть правой кнопкой мыши и в контекстном меню выбрать команду Evaluate/Modify.
Введите выражение в поле Expression так же, как в диалоговом окне Watch Properties (см рис 2.12). Затем щелкните на кнопке Evaluate для того, чтобы увидеть результат в поле Result. Если выражение состоит только из имени простой переменной (не массива или структуры!), можете ввести новое значение для переменной в поле New Value и, щелкнув на кнопке Modify, присвоить переменной новое значение. Это позволяет корректировать значения переменных в процессе отладки, не останавливаясь и не перекомпилируя всю программу, что сбережет ваше время и нервы, и даст возможность, найдя одну ошибку, искать другие, а исправлением первой ошибки заняться чуть позже.
Рис 2.14 Использование диалогового окна Evaluate/Modify для проверки изменения отдельной переменной.
Диалоговое окно Evaluate/Modify — немодальное, т. е. вы можете не закрывать его, продолжая отладку. Однако учтите, что в отличие от окна Watch List диалоговое окно Evaluate/Modify не отслеживает изменения значений переменных и для получения информации о текущем состоянии переменной вам необходимо воспользоваться кнопкой Evaluate.
Так же, как и окно Watch List, диалоговое окно Evaluate/Modify может выводить сообщения об ошибках, если отладчик не в состоянии вывести информацию о переменных. И точно так же не выводится информация о выражениях, содержащих вызов функций.
Как видите, окно Watch List и диалоговое окно Evaluate/Modify очень похожи, но каждое из них имеет свои преимущества в различных ситуациях. Обратите внимание на одно существенное отличие диалогового окна Evaluate/Modify его вывод выполняется в поле с несколькими строками, а потому оно более удобно для просмотра структур и объектов.
Directories/Conditionals
Установки Output и Unit output определяют, где компилятор размещает ЕХЕ или DLL, а также скомпилированные модули. Если оставить опции незаполненными, создаваемые модули будут располагаться там же, где и исходные тексты, а выходные выполняемые файлы или DLL — в папке проекта.
Conditional defines определяют флаги, проверяемые в процессе компиляции и используемые, как правило, для включения или исключения блоков кода из проекта при компиляции.
Unit Aliases существует для совместимости со старыми версиями Delphi (в свое время модуль Windows был разбит на два файла и для сборки одного модуля следовало присвоить один и тот же псевдоним Windows обоим файлам).
Добавление шаблонов проектов
Возможности Object Repository не ограничиваются хранением форм, модулей и диалоговых окон (даже целые проекты могут быть сохранены в виде шаблонов). Шаблон проекта может содержать формы, модули, и пользовательский код и использоваться в качестве отправной точки для создания нового проекта.
Помимо стандартных, вы, вероятно, захотите иметь и собственные шаблоны. Для этого добавьте их в Object Repository следующим образом. Создайте проект, добавьте в него код и объекты по вашему усмотрению. Сохраните проект. Вернитесь к разделу "Разделяемое хранилище", чтобы вспомнить о том, где и как хранить шаблоны. Выберите команду Project/Add To Repository, после чего будет выведено диалоговое окно Add to Repository, показанное на Рисунок 1.18. Введите запрашиваемую информацию, щелкните на кнопке ОК, и проект будет добавлен в Object Repository в качестве шаблона. Рисунок 1.18. Диалоговое окно Add to Repository позволяет добавить собственный шаблон проекта в Object Repository
Element_ActiveX
Элементы управления ActiveX
Теперь займемся увлекательным делом — создадим элементы управления ActiveX. Рассмотрим, что собой представляв ActiveX и чем элементы управления ActiveX лучше или хуже стандартных компонентов Delphi на базе библиотек VCL.
Технология AcliveX компании Microsoft представляет собой технологию ОСХ, переделанную для обеспечения пepедачи элементов управления через службы Internet, в основном через World Wide Web. Я уверен, что читатель спросит "Это все хорошо, но как технология ActiveX может помочь мне?". Но неужели вам не хочется создать элементы управления, которые можно использовать не только в Delphi, но и в таких средах программирования, как Borland C++ Builde Visual C++ и Visual Basic? Технология ActiveX предоставляет вам такую возможность.
Чтобы использовать эту возможность, Delphi предлагает с помощью среды DAX заключить потомка класса TWin Control в элемент управления ActiveX. Ниже перечислены платформы, на которых сертифицированы для выполнены элементы управления ActiveX, поставляемые с Delphi. Borland Delphi версии 2 и 3. Borland C++ Builder. Borland Paradox 8. Borland IntraBuilder. Microsoft Visual C++. Microsoft Visual Basic версии 4 и 5. Microsoft Internet Explorer 3.01. Microsoft ActiveX Control Pad. Microsoft Frontpage. Создание элемента управления ActiveX предусматривает выполнение следующих этапов. Элемент управления VCL должен быть вставлен в палитру компонентов Delphi и должен быть потомком класса TWinControl. Чтобы создать элемент управления ActiveX, являющийся потомком TGraphicControl. в большинстве случаев достаточно изменить базовый класс на TCustomControl. Этот способ использован в примере, который будет приведен немного позже. Запустите мастера ActiveX Control, выбрав команду File/New и дважды щелкнув на пиктограмме ActiveX Control во вкладке ActiveX. В окне мастера выберите компонент VCL, который нужно преобразовать в элемент ActiveX, укажите имя элемента ActiveX, включите или отключите информацию о лицензировании, версии и т.п. После щелчка на кнопке OK Delphi создаст библиотеку типов со всеми свойствами, объявленными как public и published, методами и событиями. Дополнительно создаются два файла исходного кода: в одном из них содержатся определения из библиотеки типов (интерфейсы, диспинтерфейсы и т.д.), а во втором — реализации методов интерфейса, определенных в первом файле. В процессе создания библиотеки типов Delphi может преобразовать не все конструкции языка то ли потому, что нет смысла преобразовывать отдельные конструкции, то ли потому, что она не знает, как преобразовать некоторые конструкции языка Object Pascal. Независимо от причины, по которой преобразование не выполнилось, в определение элемента ActiveX можно легко добавить любые свойства, методы и события. Для этого откройте библиотеку типов (с помощью команды View/Type Library), внесите необходимые изменения и щелкните на кнопке Refresh панели инструментов редактора библиотек типов, что позволит Delphi обновить код. Создайте код всех методов Get и Set, в которых не реализована необходимая функциональность или которые оставлены пустыми, как добавления к библиотеке типов. Это относится и к обработчикам событий. Теперь вы, возможно, пожелаете создать одну или несколько страниц свойств. В средах, не поддерживающих инспекторов свойств, это даст возможность пользователю, щелкнув правой кнопкой мыши, вызвать некоторое подобие инспектора свойств для изменения значений свойств элемента управления. Для этого, как минимум, нужно создать основную страницу свойств. Delphi обладает несколькими стандартными страницами свойств для управления цветом, шрифтами, изображением и списком строк Delphi. Откомпилируйте и зарегистрируйте элемент управления. ОСХ-файл можно импортировать в любую среду разработки, поддерживающую элементы управления ActiveX, которые созданы в Delphi.
Error Setting Debug Exception Hook
Если вы увидели сообщение, показанное Рисунок 2.26, значит, отладчик оказался в трудном и, главное, нестабильном положении, что обычно происходит после аварийной остановки отлаживаемого приложения. Что делать? Попробуйте воспользоваться командой Run/Program Reset и запустить приложение еще раз. Не помогло? Выполните команду Program/Build All. И это не дает результата? Тогда вам придется выйти из среды разработки и запустить ее еще раз. Самый последний совет — проделать то же и с операционной системой... Рисунок 2.26. Сообщение Error setting debug exception hook
External Exceptions
Сообщение об ошибке External exception, показанное на Рисунок 2.27, может вызываться порожденной приложением исключительной ситуацией, перехваченной He-Delphi-модулем (DLL). Коды ошибок определены в файле WINDOWS.PAS, включенном в поставку Delphi; их символические имена имеют вид STATUS_xxxxx. Например, показанная на Рисунок 2.28, исключительная ситуация C000001D— исключительная ситуация STATUS_ILLEGAL_INSTRUCTION. Это, конечно, позволяет судить о том, что произошло, но не дает никакой информации о том, где это произошло, так что единственный способ найти ошибку — разделять и властвовать, т.е. пересмотреть приложение до возникновения исключительной ситуации. Рисунок 2.27. Сообщение о нарушении доступа Рисунок 2.28. Сообщение о внешней исключительной ситуации
Фабрики объектов в среде DAX
Delphi предоставляет клиентам (контроллерам) объекты серверов СОМ посредством использования фабрик (factories) объектов, которые регистрируются с глобальным сервером СОМ Delphi СОМ Server (ComServer), определенным в модуле ComServ. Сервер СОМ Delphi обрабатывает все запросы объектов СОМ и создает запрашиваемые объекты, если такие зарегистрированы. Классы фабрик объектов используются для поддержки объектов СОМ, типизированных объектов СОМ, объектов автоматизации элементов управления, которые показаны ниже в порядке наследования.
TComObjectFactory
TActiveXPropertyPageFactory
TTypedComObjectFactory
TAutoObj ectFactory
TActiveXControlFactory
TActiveFormFactory
Классы TActiveXControlFactory и TActiveFormFactory используются для регистрации элементов управления ActiveX и форм ActiveForm.
Что же делают эти объекты? Они инкапсулируют интерфейс IClassFactory, используемый для создания объектов с ответствующего класса каждый раз, когда клиентское приложение запрашивает определенный объект. На эти классы также возложена ответственность за внесение записей в реестр Windows в соответствии с типом класса фабрики. Например, кла TActiveXControlFactory вносит в реестр информацию о сервере ActiveX и соответствующей библиотеке типов.
Получив представление о средствах Delphi, поддерживающих разработку объектов ActiveX, рассмотрим инструмент Delphi для создания объектов ActiveX. Как это ни странно, но начать придется не с кодирования, а с документации. Если быть более точным, то сначала придется создать библиотеку типов, которая будет использоваться в Delphi для генерации шаблон кода, базирующегося на иерархии классов среды DAX. Позже созданный шаблон будет заполнен, откомпилирован, зарегистрирован и т.д. Следовательно, прежде чем приступить к созданию объектов ActiveX, следует усвоить, что такое информаци о типах и как использовать редактор библиотек типов (Type Library Editor) Delphi. Обозначенные аспекты описаны в следуй щем разделе.
Файловый тип
Тип file предназначен для доступа к линейной последовательности элементов, которые могут представлять данные любого типа, кроме содержащих типы file и class. Объявление файлового типа подобно объявлению массива, только без указания числа элементов.
file of Typel // Файл определенного типа, содержащий
// записи фиксированной длины.
file // Файл без типа или "блочный".
textfile // Файл с записями переменной длины, разделенными символами CR
//и LF ("возврат каретки" и "новая строка").
Механизм ввода-вывода информации как никакой другой аспект программирования зависит от языка и реализации. В большинстве случаев предполагается, что программисту незачем вникать во внутреннюю структуру переменных, управляющих вводом-выводом, и при передаче информации следует полностью полагаться на предназначенные для этого процедуры. Их реализация должна оставаться чем-то наподобие черной магии. В Basic файлы обозначаются числовыми значениями — дескрипторами. В C/C++ программисты манипулируют указателями на структуру FILE. И только в Delphi файловая структура — это переменная.
Фиксированные записи
В фиксированной части записи определяется одно или несколько независимых полей. Каждому полю обязательно присваивается имя и тип:
record
fieldnamel: fieldtypel;
fieldname2, fieldname3: fieldtype2;
end;
Имея доступ к информации в записи, можно обрабатывать всю запись целиком (все поля одновременно) или только отдельное поле. Для обращения к отдельному полю наберите имя записи, точку и идентификатор поля, например
MyRec.Fieldnamel
Для доступа ко всей записи просто укажите ее имя.
Совет: В языке С эквивалентом фиксированного типа record из Delphi является struct. В C++ также определен тип struct, синтаксис которого совместим с типом struct из С. Однако в C++ этот тип имеет дополнительные особенности, благодаря чему напоминает тип Class из Delphi.
Forms
Раскрывающийся список Main form определяет главную форму вашего приложения, которую Delphi выводит первой и закрывает, когда приложение завершает работу.
Два списка. Auto-create forms и Available forms, позволяют определить автоматически создаваемые формы.
Функции Windows API для консольного приложения
Вы можете делать гораздо больше в консольном приложении, чем просто считывать и выводить текст. Вам доступно около 40 функций Windows API для работы с дисплеем, например изменение атрибутов выводимых символов или изменение размеров консоли.
В листинге 1.1 изменяется текст, выводимый в заголовке консольного окна. Вы можете найти этот пример на дополнительно распространяемой дискете в папке EgConsoleTitle.
Листинг 1.1. Изменение заголовка консольного окна
program EgConsoleTitle;
{$APPTYPE CONSOLE}
uses
Windows, SysUtils;
{$R *.RES}
var
sNewTitle, sErrMsg: String;
begin
sNewTitle:= 'Welcome to Con5ole World';
if not SetConsoleTitle(PChar(sNewTitle)) then
begin
sErrMsg:= 'Unable to set caption - '+SysErrorMessage(GetLastError);
MessageBox(0,PChar(sErrMsg),'Error',MB_ICONEXCLAMATION+MB_OK);
end;
ReadLn;
end.
Здесь функция API SetConsoleTitle возвращает False, если назначить новый заголовок невозможно. GetLastError возвращает числовое значение последней ошибки API, которое SysErrorMessage конвертирует в строку для вывода на экран.
Совет: Поставляемая с Delphi система справки по Win32 содержит функции консольного API. Для доступа к ним выведите пункт Console Reference и щелкните на кнопке.
Функция Windows API для консольного приложения
А теперь забудьте обо всех этих кнопочках, пиктограммах и пропахших мышами приложениях — настоящие программисты и пользователи обожают текстовый режим! Если же говорить серьезно, иногда программы в текстовом режиме могут быть полезными. Примерам таких программ несть числа, и основная проблема всегда заключалась в том, что они запускались в окне DOS и не имели доступа к Windows API или к таким ресурсам, как принтеры и память (не говоря уже о том, что это были 16-разрядные приложения с 640-килобайтовым ограничением на используемую память). Теперь, когда Delphi поддерживает создание консольных приложений, можно создавать 32-разрядные текстовые приложения с использованием линейной модели памяти и с возможностью работать с Windows API и применять ресурсы
Обычно консольные приложения используются, когда не нужно вводить информацию большого объема и требования к интерфейсу сведены к минимуму. Ну и заметим к тому же, что простенькое "Hello, world!", созданное путем размещения компонента TLabel в форме, будет иметь размер около 150 Кбайт, в то время как консольное "Hello, world!" уложится в 10...
Информация о библиотеке типов
Вкладка Attributes в описании библиотеки типов
Вкладка Uses в описании библтотеки типов
Информация о библиотеке типов
Каждая библиотека типов включает раздел, описывающий саму библиотеку, — элемент библиотеки типов. Когда этот элемент выбран в панели Object List (см. Рисунок 3.1), в правой части экрана редактора библиотеки типов появляются две вкладки: Attributes и Uses. Два следующих раздела описывают назначение вкладок Attributes и Uses в элементе библиотеки типов.
Вкладка Attributes в описании библиотеки типов
Во вкладке Attributes (атрибуты) содержится общая информация и характеристики библиотеки типов. Показанные ниже атрибуты и флаги появляются в этой вкладке, когда в панели Object List выбран элемент библиотеки типов (см. Рисунок 3.1). В табл. 3.1 перечислены все атрибуты, а в табл. 3.2 — все флаги библиотеки типов.
Использование директивы Assert
Оператор Assert— новый оператор в Delphi 4. В действительности это просто тест на логическую истину/ложь. При использовании этого оператора вы убеждаетесь, что логическое выражение истинно, если при выполнении выражение становится ложным, генерируется исключительная ситуация. Синтаксис использования оператора таков:
Assert (<логическое выражение)
Вы можете использовать проверку, например, в начале процедуры для выяснения корректности параметров, как показано ниже.
procedure Foo(Count: Cardinal);
begin
Assert(Count < SizeOf(Word));
end;
Если выражение окажется ложным, появится сообщение об ошибке, подобное показанному на Рисунок 2.3. Конечно же, у вас уже вертится на языке вопрос, чем же это отличается от конструкции if... else. Дело в том, что управлять генерацией кода для оператора Assert очень легко и просто с помощью директивы компилятора. Для применения описанных возможностей используйте директиву $ASSERTIONS ON или $С +, а для отключения действия Assert— $ASSERTIONS OFF или $С - (при этом компилятор игнорирует операторы Assert и код для них не генерируется).
Поскольку вы явно захотите включить эту возможность в отладочную версию и исключить ее из коммерческой, используйте код, подобный приведенному ниже.
{$ifdef Debug}
($ASSERTIONS ON)
{$else}
($ASSERTIONS OFF)
{$endif}
Какого типа выражения могут использоваться в операторе Assert? Любого (конечно, если оно возвращает логическое значение). Однако тут есть свои маленькие подводные камушки, о которые легко поцарапаться. Представьте себе, что у вас есть некоторая функция, например выясняющая, сколько раз она была вызвана.
function CountMe: Integer;
const ReferenceCount: Integer = 0;
begin
Inc(ReferenceCount);
Result:= ReferenceCount;
end;
Предположим, что вы вызываете ее в операторе Assert. Таким образом, в коммерческой версии, которая игнорирует все операторы Assert, количество вызовов функции будет не таким, как в отладочной версии. Так что будьте внимательны и осторожны.
Использование модулей выполнения
Наиболее важный момент в этом процессе — распространение модулей выполнения (runtime packages). Если элемент управления ActiveX или форма ActiveForm использует модули, то следует включить опцию Deploy Required Packages.
Использование отладчика TD
32.EXE
Может случиться, что, несмотря на все возможности отладчика Delphi 4, вы столкнетесь с совершенно неотслежимаемой ошибкой. Если это произойдет, примите мои искренние соболезнования. Можете попытаться использовать отдельный отладчик фирмы Borland (TD32.EXE), поставляемый в комплекте Turbo Assembler или Borland C++. В дополнении ко всему, что есть в отладчике Delphi, TD32 имеет и то, чего в Delphi нет. В частности, TD32 позволяет установить аппаратные точки останова, что означает, например, останов при обращении к портам ввода-вывода или к памяти. Это тяжелая работа, и я могу только посочувствовать вам, если вы за нее беретесь. И учтите, что такая работа требует знания как системного программного, так и аппаратного обеспечения.
Использование отладчика TD32 EXE
Прежде чем начать искать ошибки, стоит определить, где именно они водятся. Перед поиском ошибок скопируйте свой проект и работайте с копией. В крайнем случае, когда вы запутаетесь окончательно, вы сможете вернуться к тому, с чего начинали. Только не забудьте между исправлением ошибки и удалением рабочей копии проекта внести изменения в основной проект!
Не забывайте и о комментариях, которые позволяют если не найти ошибки, то хотя бы отследить, что и когда вы делали.
Учитесь у великих! Правило "Разделяй и властвуй" еще никто не отменял. А потому разделяйте свою задачу на части и властвуйте. Найти ошибку или убедиться в ее отсутствии в части программы проще, чем во всей программе в целом, особенно когда появляется эффект интерференции ошибок, при котором ошибка начинает взаимодействовать с другой так, что отследить их становится очень сложно.
И, тем не менее, при отладке программисты нередко оказываются в почти безвыходной ситуации. Некоторые ошибки я и опишу в этом разделе и подскажу, как успешно их поймать.
Использование подписей кода
Если вы хотите подписать проект элемента управления ActiveX или формы ActiveForm, то сначала включите опцию Code sign project, а после этого во вкладке Code Signing диалогового окна Web Deployment Options введите следующий текст.
Have obtained a Software Publishing Certification file (.SPC)
Private Key (.PVK)
Что такое подпись кода? Это технология, встроенная в Microsoft Internet Explorer, известная как Authenti-code™. Она позволяет приложению Internet Explorer вместо стандартного предупреждения об использовании неизвестного приложения отображать сообщение о сертификате подлинности программного обеспечения в момент загрузки выполняемого кода, например программ установки через Internet, элементов управления ActiveX или форм ActiveForm. Подпись кода—это цифровой идентификатор (Digital ID) в компании VeriSign (www.verisign.com) с целью получения сертификата на публикацию программного обеспечения Software Publishing Certificate и частного ключа Private Key, используемого для подписи всех распространяемых компанией выполняемых файлов. С помощью подписи кода разработчик может обеспечить пользователей информацией и страховкой при загрузке программного обеспечения из Internet.
Никаких требований, заставляющих разработчиков подписывать выполняемый код, не существует, но эта процедура предоставляет способ повышения надежности загрузки программного обеспечения из Internet. Более подробную информацию о получении цифрового идентификатора VeriSign Software Publisher Digital ID можно получить на Web-узле компании VeriSign по адресу http://www.verisign.com. Внимание! Пожалуйста, запомните, что регистрация программного обеспечения производится для выбранного компьютера. Это значит, что регистрироваться для получения идентификатора Digital ID, получать идентификатор Digital ID и использовать этот идентификатор нужно на одном компьютере и в одной версии Internet Explorer. Если службу Authenticode планируется использовать более чем на одном компьютере, то нужно получить более чем один идентификатор Digital ID. Если установлена новая версия Internet Explorer, то нужно выполнить повторную регистрацию.
Использование шаблонов проектов
Delphi поставляется с тремя шаблонами проектов. MDI Application создает полностью функциональный MDI-проект. Родительская форма включает меню, кнопки Speed Button и строку состояния. Проект также содержит замещаемый код, реализующий функции меню и управляющий сообщениями в строке состояния. SDI Application содержит простой SDI-проект. Основная форма содержит меню, кнопки Speed Button и строку состояния. В проект также включено диалоговое окно About и реализующий его код. Win95 Logo Application создает проект, моделирующий основные принципы, которые установлены Microsoft для нее Win95 Logo certification. Если вы будете придерживаться этого стиля и получите сертификат Microsoft, то сможете вывести логотип "Designed for Win95". Для создания нового SDI-поиложения с использованием шаблона выполните следующие действия.
Выберите команду File/New, и появится диалоговое окно New Items (см. Рисунок 1.6). Щелкните на вкладке Projects, что приведет к появлению соответствующей страницы. Выберите пиктограмму SDI Application, щелкните на кнопке ОК, и появится диалоговое окно Select Directory, показанное на Рисунок 1.16. Определите каталог для нового проекта. Если вы выберете несуществующий каталог, Delphi создаст его для вас. Щелкните на кнопке ОК, и новый проект будет создан. При создании проекта доступна только опция Copy. Все файлы проекта копируются в ваш каталог и изменения в них не приведут к изменению шаблона проекта в хранилище. На странице Projects содержится еще одна пиктограмма— Application Expert. Эксперт приложений, показанный на Рисунок 1.17, проведет вас через все этапы создания нового проекта. Рисунок 1.16. Диалоговое окно Select Directory позволяет выбрать каталог для нового проекта. Рисунок 1.17. Application Expert поможет вам создать новый проект.
Использование сжатия CAB
Пользователю предоставляется возможность использовать опцию Use CAB file compression, которая позволяет сжать элемент управления ActiveX или форму ActiveForm и любые дополнительные файлы, такие как файлы DPL или файлы лицензий. Эта возможность поддерживается только при использовании броузера Internet Explorer 3.01. На сегодняшний день броузер Netscape Navigator не поддерживает САВ-файлы. По умолчанию библиотека ActiveX, модули (если такие есть) и дополнительные файлы упаковываются в три САВ-файла. Возможна также упаковка всех файлов в один САВ-файл, но делать это не рекомендуется. Разделяя файлы по различным архивам CAB, вы можете избежать загрузки излишних файлов, которые уже установлены на компьютере пользователя.
Элементы Interface и Displnterface в библиотеках типов
Вкладка Attributes в описании интерфейса
Вкладка Members в описании интерфейса
Вкладка Attributes в описании свойств и методов интерфейса
Элементы Interface и Displnterface в библиотеках типов
Интерфейс в библиотеке типов — это коллекция определении свойств и методов. Клиент может получить доступ к интерфейсам либо посредством таблицы виртуальных методов, либо с помощью специального интерфейса OLE I Dispatch, который позволяет использовать свойства и методы объектов через уникальный идентификатор или DispID. Элемент Displnterface определяет интерфейс, доступ к которому можно получить только через интерфейс IDispatch. Двойной интерфейс (dual interface) — это интерфейс, доступ к которому можно получить и через таблицу виртуальных методов, и через интерфейс IDispatch. В следующих двух разделах рассматривается назначение вкладок Attributes и Members в описании интерфейсов.
Вкладка Attributes в описании интерфейса
На Рисунок 3.4 показан интерфейс, выбранный в окне редактора библиотек типов, с вкладкой Attributes в правой части окна редактора. В табл. 3.4 перечислены все возможные атрибуты, которые можно использовать в описании интерфейса, а в табл. 3.5 — все флаги, используемые в описании интерфейса.
Компонент TApplication
Компонент TApplication инкапсулирует приложения при выполнении. Delphi автоматически создает экземпляр Application класса TApplication при выполнении приложения. Для использования этого объекта включите модуль Forms в раздел uses.
Компонент TScreen
Класс TScreen инкапсулирует состояние экрана или выводимой области. Delphi во время работы автоматически создаст экземпляр класса Screen. Для его использования в раздел uses нужно включить модуль Forms.
Linker
Опция Map file полезна для тех программистов, которые интересуются технической информацией, например адресами сегментов, стартовым адресом программы и т.п.
Linker output определяет, что именно будет выдавать компилятор — Delphi Compiled Unit (DCU) или объектные файлы (OBJ). Последние могут применяться, например, при разработке программ с использованием двух языков.
Опции ЕХЕ и DLL позволяют создавать консольные приложения, описанные в разделе "Создание консольного приложения", и включать отладочную информацию, о которой подробно рассказывается в главе 2, "Тестирование и отладка".
Memory sizes определяет минимальный и максимальный размеры стека приложения и устанавливает предпочтительный базовый адрес для вашей DLL. Если ваша программа часто использует глубокую рекурсию или большие локальные переменные, увеличьте размер стека. Базовый адрес лучше не изменять, если вы не совсем уверены в том, что собираетесь делать.
ЕХЕ Description представляет возможность внести в ЕХЕ или DLL строку описания приложения (что-то вроде "MixFix (С) 1993-1997 KIV without Co").
Модуль протоколирования отладочной информации
unit uLoq;
interface
procedure Log(S: Strings-implementation uses
Windows, SysUtils;
var
LogFile: TextFile;
LogCriticalSection: TRtlCriticalSection;
procedure Log(S: String);
var
SystemTime: TSystemTime;
FileTime: TFileTime;
begin
GetSystemTime (SystemTime) ;
SystemTimeToFileTime(SystemTime, FileTime) ;
EnterCriticalSection(LogCriticalSection);
WriteLn(LogFile, Format('%s %.8x%.8x %5',
[FormatDateTime('yy.mm.dd hh.inm.ss'. Now),
FileTime.dwHighDateTime, FileTime.dwLowDateTime, S])) ;
LeaveCriticalSection(LogCriticalSection) ;
end;
procedure Startup;
var
FileName: String;
begin
InitializeCriticalSection(LogCriticalSection);
FileName := Format("Log file for %s at %s.txf,
[ParamStr(O), DateTimeToStr(Now)]) ;
while Pos(':', FileName) 0 do
FileName[Pos(':', FileName)] := '.';
while Pos('/', FileName) 0 do
FileName[Pos('/', FileName)] := '-';
while Pos('\', FileName) 0 do
FileName[Pos('\', FileName)] := '.';
AssignFile(LogFile, FileName);
Rewrite(LogFile) ;
end;
procedure Shutdown;
begin
CloseFile(LogFile) ;
DeleteCriticalSection(LogCriticalSection) ;
end;
initialization Startup;
finalization Shutdown;
end.
Этот модуль сам создает, открывает и закрывает файл протокола. Имя файла создается с учетом имени приложения и текущих даты и времени, что исключает возможность записи информации поверх существующего файла. Для использования модуля условно включите его, как показано ниже.
unit MyUnit;
interface
uses
($ifdef Debug} uLog, {$endif)
Windows, Messages, SysUtils, Classes,
. . .
Затем используйте его приблизительно так.
{$ifdef Debug)
Log(Format('Entering the Foo procedure; Bar = %d',[Bar]));
{$endif}
He забывайте размещать вызов между директивами условной компиляции, иначе при компиляции коммерческой версии возникнет ошибка.
Модуль uLog обладает двумя интересными и полезными свойствами. Во-первых, каждая запись в файл предваряется информацией о дате, времени и шестнадцатеричным числом, соответствующим системному времени в миллисекундах. Эта информация может быть весьма полезной, особенно когда вы хотите отследить последовательность событий в приложении. Во-вторых, модуль использует критические разделы (critical section), что обеспечивает доступ к файлу только одной подзадачи в один момент времени.
На Рисунок 2.25 показан типичный файл протокола в программе Notepad.
Как правильно использовать файл протокола? Какую информацию в него записывать? Сколько программистов, столько и ответов на эти вопросы. Лично я предпочитаю придерживаться золотой середины между "записывай все" и "записывай только то, что отлаживаешь".
Make_ActiveForm
Создание форм ActiveForm
Форма ActiveForm — это набор визуальных или невизуальных компонентов для создания смешанного элемента управления ActiveX. Другими словами, визуальные элементы управления ActiveX можно совмещать для создания простого комбинированного элемента ActiveX. Кроме того, можно создавать целые приложения, обладающие одной формой. которые будут распространяться как формы ActiveForm.
Использование невизуальных элементов означает, что в формах ActiveForm можно использовать объекты BDE для доступа к базам данных, или, что еще лучше, для доступа к данным можно использовать технологию брокеров данных производства Borland (Data Broker), которая разрабатывалась специально для доступа к распределенным множествам данных в рамках сети, такой как Internet.
Что касается выполняемых в формах ActiveForm действий, то тут нет каких-то особых ограничений. Единственным отличием от стандартных форм Delphi является то, что клиентам предоставляются только те свойства, методы и события, которые связаны непосредственно с формой ActiveX. Это означает, что все свойства, методы и события компонентов VCL, используемых в форме, не будут предоставляться клиентам. Названные элементы являются внутренними по отношению к форме ActiveForm.
Если необходимо предоставить клиентам доступ к внутренним свойствам компонентов VCL, то следует добавить в форму ActiveForm новые свойства и методы, как в процессе создания элемента управления ActiveX. За исключением таких случаев, методы свойства Get и Set будут получать и устанавливать значения внутренних компонентов, а не основ-ного элемента управления VCL.
Процесс создания формы ActiveForm в основном совпадает с процессом создания элемента управления ActiveX, но для ясности ниже перечислены основные его этапы:
Make_ActiveX
Создание элементов управления ActiveX
Большинству пользователей устышавших термин АсtiveX на ум сразу приходят элементы управления ОСХ по-новому названные в очередном маркетинговом трюке компании Microsoft. В некотором смысле это так Microsoft действительно использует термин ActiveX дтя обозначения технологии базирующихся на модели СОМ. Но аббревиатура OLE уже не охватывает всех этих технологий. Microsoft настолько расширила определение OLE, что разработчики уже чавно считают сопутствующие технологии чем-то отдельным от OLE. Поэтому в профессиональную лексику был введен термин ActiveX, используемый для обозначения всех базирующихся на СОМ технологии, которые существуют на данный момент и будут созданы в будущем. На сегодняшний день ActiveX включает следующие технологии:
Библиотеки ActiveX (внутренние серверы) Серверы автоматизации (внутренние и внешние серверы) Элементы управления ActiveX Формы ActiveForm (комбинированные элементы управления ActiveX) Страницы свойств
Массивы
Массивы могут быть одно- или многомерными, как в следующем примере.
array [ordinal_type] of type_definition;
array [ordinal typel, ordinal type2] of type definition;
Каждый массив содержит некоторое количество элементов информации одного типа. Для обращения к элементу массива надо указать имя массива и индекс элемента, заключенный в квадратные скобки. Обратите внимание, что число элементов массива в каждом измерении задается порядковым типом (ordinal_type). Для этого можно воспользоваться идентификатором некоторого типа (например, Boolean или AnsiChar), однако на практике обычно явно задается поддиапазон целых.
Количество элементов массива равно произведению количеств элементов во всех измерениях.
Для обращения к элементу массива укажите имя этого массива и индекс элемента в квадратных скобках. Пусть, например, массив определен следующим образом:
var MyArray: Array [1..10] of Integer;
Тогда обращение к его третьему элементу будет выглядеть, как MyArray[З], и выполняться, как к переменной Integer.
Совет: Понятие массива существует в большинстве языков программирования, однако синтаксис, как правило, в каждом случае свой. В языках Basic и FORTRAN вместо квадратных скобок применяются круглые. В С и C++ элементы массива нумеруются с нуля, в FORTRAN — с единицы. В Basic нумерация начинается с нуля или единицы, в зависимости от результата последнего выполнения оператора OPTION BASE. В некоторых версиях Basic можно задавать верхнее и нижнее значения индексов, как это делается в Delphi. В С и C++ обращение к массиву эквивалентно обращению к его первому (нулевому) элементу. В Delphi это будет обращением ко всему массиву.
MDIметоды TFor
m
Специфичные для MDI-форм методы перечислены ниже.
Arrangelcons выстраивает пиктограммы минимизированных дочерних форм в нижней части родительской формы.
Cascade располагает дочерние формы каскадом, так что видны все их заголовки.
Next и Previous переходит от одной дочерней формы к другой, как будто вы нажали <Ctrl+Tab> или <Ctrl+Shift+Tab>.
Tile выстраивает дочерние формы так, что они не перекрываются.
MDIсобытия TForm
В MDI-приложении событие OnActivate запускается только при переключении между дочерними формами. Если фокус ввода передается из не MDI-формы в MDI-форму, генерируется событие OnActivate родительской формы, хотя ее свойство Active никогда и не устанавливается равным True. Эта странность на самом деле строго логична: ведь, если бы OnActivate генерировался только для дочерних форм, не было бы никакой возможности узнать о переходе фокуса ввода от другого приложения.
MDIсвойства
TForm ActiveMDIChild MDIChildren и MDIChildCount TileMode WindowMenu
MDIсвойства TForm
Объект TForm имеет несколько свойств, специфичных для MDI-приложений.
ActiveMDIChild
Это свойство возвращает дочерний объект TForm, имеющий в текущее время фокус ввода. Оно полезно, когда родительская форма содержит панель инструментов или меню, команды которых распространяются на открытую дочернюю форму.
Например, представим, что проект использует дочернюю форму, содержащую элемент TMemo, названный memDailyNotes. Имя класса этой дочерней формы— TfrmMDIChild. Родительская форма содержит кнопку Clear в панели инструментов, которая удаляет содержимое memDailyNotes в активной дочерней форме. Вот как это реализуется.
procedure TfrmMDIParent.spbtnClearClick(Sender: TObject);
begin
if not (ActiveMDIChild = Nil) then
if ActiveMDIChild is TfrmMDIChild then
TfrmMDIChild(ActiveMDIChild).memDailyNotes.Clear;
end;
В первой строке проверяется, равен ли ActiveMDIChild значению Nil, так как в этом случае обращение к объекту вызовет исключительную ситуацию.
Совет: ActiveMDIChild равен Nil, если нет открытых дочерних форм или свойство FormStyle не равно fsMDIForm.
Поскольку ActiveMDIChild возвращает объект TForm, компилятор не имеет доступа к memDailyNotes — объекту TfrmMDIChild. Вторая строка проверят соответствие типов, т.е. действительно ли ActiveMDIChild указывает на объект TfrmMDIChild.
Третья строка выполняет преобразование типа и вызывает метод Clear компонента memDailyNotes.
MDIChildren и MDIChildCount
Свойство MDIChildren является массивом объектов TForm, предоставляющих доступ к созданным дочерним формам. MDIChildCount возвращает количество элементов в массиве MDIChildren.
Обычно это свойство используется при выполнении какого-либо действия над всеми открытыми дочерними формами. Вот код сворачивания всех дочерних форм командой Minimize All.
procedure TFormI.mnuMinimizeAllClick(Sender: TObject);
var
iCount: Integers;
begin
for iCount:= MDIChildCount-1 downto 0 do
MDIChildren[iCount].WindowState:= wsMinimized;
end;
Если вы будете сворачивать окна в порядке возрастания элементов массива, цикл будет работать некорректно, так как после сворачивания каждого окна массив MDIChildren обновляется и пересортировывается, и вы можете пропустить некоторые элементы.
TileMode
Это — свойство перечислимого типа, определяющее, как родительская форма размещает дочерние при вызове метода Tile. Используются значения tbHorizontal (по умолчанию) и tbVertical для размещения форм по горизонтали и вертикали.
WindowMenu
Профессиональные MDI-приложения позволяют активизировать необходимое дочернее окно, выбрав его из списка в меню. Свойство WindowMenu определяет объект TMenuItem, который Delphi будет использовать для вывода списка доступных дочерних форм.
Для вывода списка TMenuItem должно быть меню верхнего уровня. Это меню имеет свойство Caption, равное swindow.
Методы TApplication
Minimize и Maximize ProcessMessages Terminate
Методы TApplication
Minimize и Maximize
Эти методы заставляют приложение принять свернутый и развернутый вид. Вы можете удивиться, зачем это необходимо при наличии свойства WindowState объекта TForm. Однако так вы можете минимизировать форму на рабочем столе, но не в панели задач. Описываемый здесь метод осуществляет эту операцию более корректно.
ProcessMessages
Этот метод вынуждает приложение обработать ожидающие сообщения. Это полезно делать, например, в длинном цикле, чтобы приложение могло реагировать на поступающие сообщения. Предположим, ваше приложение состоит из цикла
while not Application.Terminated do
При этом оно не сможет обрабатывать сообщения, а вы не сможете перемещать окна и воздействовать на его элементы управления. Одним словом, несмотря на деятельность в цикле, приложение оказывается мертвым для внешнего мира. Однако все изменяется при таком варианте кода:
while not Application.Terminated do
Application.ProcessMessages;
Совет: Программисты часто предпочитают использовать для длительных вычислений отдельную подзадачу, реализуемую классом TThread.
Terminate
Этот метод— предпочтительный способ закрытия приложения. Terminate не закрывает приложение немедленно, а дожидается обработчика события и завершения всех других процессов. Обычно оно используется в меню File/Exit.
procedure TFormI.mnuFileExitClick(Sender: TObject);
begin
Application.Terminate;
end;
Множества
Зарезервированное слово set (множество) определяет множество не более чем из 256 порядковых значений:
Set of ordinal type
Минимальный и максимальный порядковые номера исходного типа (на основе которого определяется множественный тип) должны быть в пределах между 0 и 255. Переменная множественного типа содержит (или не содержит) любое значение исходного порядкового типа. Каждое значение из заданного диапазона может принадлежать или не принадлежать множеству. Рассмотрим следующий пример.
Type CharSet = set of AnsiChar; // Тип множества символов. ANSI.
var MyAlphaSet: CharSet; // Переменная типа CharSet.
Переменная set может содержать все элементы множества или не содержать ни одного. При присвоении значения переменной множественного типа элементы множества (порядковые значения) указываются в квадратных скобках:
MyAlphaSet := ['А', 'Е', 'Г, 'О', 'U', 'Y']; // Все прописные гласные.
Пустые квадратные скобки задают пустое множество, не содержащее ни одного элемента. Это относится ко всем множественными типам.
Совет: Во многих языках структурный множественный тип отсутствует. Вместо него можно применять что-либо наподобие битовых образов или битовых полей.
Модульное тестирование
Тестирование и отладка идут рука об руку, так что большинство программистов просто не воспринимают их как отдельные этапы разработки программ. Однако путь к успеху лежит через разделение процесса отладки и тестирования на два разных этапа работы над программой, и вам следует четко представлять себе, что цель тестирования — определить наличие (или отсутствие) ошибок, В то время как цель отладки — определить местоположение ошибок и устранить их. Поскольку цели этих двух этапов разработки программ различны, различны и используемые для этого методы и инструменты.
Модульное тестирование
Тема модульного тестирования обширна и многообразна, и писать о ней можно много, но я ограничусь буквально несколькими словами. Кстати, когда речь идет о модульном тестировании, слово модуль не имеет отношения к концепции модулей Delphi и подразумевает функцию, подсистему или другой хорошо определенный программный модуль.
Коротко говоря, идея модульного тестирования состоит в разбивке приложения на функциональные единицы и тестировании каждой из них по отдельности. Это часто означает написание одного или нескольких небольших приложений-оболочек, цель создания которых — отработать один из модулей вашего приложения. Ваша задача — выявить все возможные ошибки, так как сообщения о внутренних ошибках программы, допустимые в тестовых версиях, недопустимы в коммерческих. Сообщение об ошибках во время работы коммерческой версии приложения эквивалентны сообщению, приведенному на Рисунок 2.4 Рисунок 2.4. О чем думают пользователи, когда ваша программа выводит сообщение об ошибке?..
Ну, и наконец, философское замечание о следствии из закона Мэрфи для программирования "В любой работающей программе есть хотя бы одна ошибка, при исправлении которой вносится, по крайней мере, еще две ошибки". Даже в наилучших коммерческих программах содержатся ошибки, а потому вопрос о нахождении и исправлении всех ошибок не может даже быть поставлен. Но следует сделать все возможное, чтобы обнаружить и ликвидировать как можно больше ошибок. Следующий раздел этой главы посвящен описанию инструментов Delphi для локализации и исправления ошибок. ">
Наследование форм
Использование наследования форм Преимущества наследования форм
Подобно фундаменту здания, форма представляет собой фундамент программы, на котором строится все остальное. Форма — это место, где пользователь общается с программой. Приложение может иметь несколько форм, каждая из которых выполняет свое особое предназначение.
Delphi инкапсулирует концепцию форм в класс TForm, и каждая форма, создаваемая с помощью конструктора форм (Form Designer), наследует основные свойства, методы и события этого класса.
Класс TForm не является прямым потомком TWinControl.
Настройка IDE для отладки
Для работы со встроенным отладчиком Delphi 4 его интегрированная среда разработки (IDE) предлагает целую серию установок, большинство из которых вам лучше не трогать, а оставить, как есть (по умолчанию). Однако если вы все-таки решили изменить установки, выберите команду Tools/Options и в появившемся диалоговом окне Environment Options щелкните на вкладке Preferences (она показана на рис 2.5)
Ниже перечислены опции вкладки Preferences и их функции. Integrated Debugging. Позволяет включать и отключать встроенный отладчик. Если вы отключите отладчик, отладочные команды в меню Run станут недоступными.
Совет: Если у вас возникли проблемы при запуске приложения из интегрированной среды, возможно, это вызвано конфликтом встроенного отладчика с другими приложениями, запущенными в системе. Попробуйте отключить встроенный отладчик и, если это поможет, будьте уверены — проблема кроется в одном из уже запущенных в системе приложений. Лучшим решением будет перезагрузка и последующий запуск только абсолютно необходимых приложений.
Рисунок 2.5. Использование вкладки Preferences для настройки интегрированного отладчика Delphi. Step Program Block. Эта опция определяет, должен ли отладчик останавливаться перед началом выполнения основного блока begin. . . end при трассировке программы. Обычно данная опция отключена, и включать ее имеет смысл при добавлении кода в основной блок программы либо при отладке консольного приложения. Hide Designers on Run. Когда эта опция включена, окно Object Inspector и формы, использующиеся при разработке приложения, перед запуском программы на выполнение закрываются. Отключение опции позволяет запускать программу быстрее, но эффект перекрывается используемыми незакрытыми ресурсами приложения. Впрочем, будет ли выбрана эта опция, зависит от пользователя. Break on Exception. При включенной опции IDE всегда перехватывает исключительные ситуации и выводит окно сообщения, даже если в программе исключительная ситуация обрабатывается блоком try. . .except. Включение этой опции упростит отладку, так как выводимые сообщения при этом будут более информативными, чем сообщения обработчика, установленные по умолчанию (сравните Рисунок 2.6 и 2.7). Помимо этого, IDE размещает окно редактора поверх остальных и выделяет строку, вызвавшую исключительную ситуацию. Рисунок 2.6. Сообщение об исключительной ситуации при включенной опции Break on Exception Рисунок 2.7. Сообщение об исключительной ситуации, выводимое обработчиком по умолчанию
Совет: Конечно, опция Break on Exception полезна, но может привести в растерянность новичка в Delphi, особенно когда сообщается об исключительной ситуации, которую должен обработать блок try. . .except. Вы можете либо отключить эту опцию, либо запустить приложение не из среды Delphi, чтобы увидеть его работу глазами конечного пользователя.
Minimize on Run Опция сворачивает окно IDE при запуске приложения Подобно опции Hide Designers on Run, ее установка зависит исключительно от личных предпочтений программиста На странице Display диалогового окна Environment Options есть еще одна установка— опция Visible Gutter. Она включает или отключает отображение серой вертикальной полосы, расположенной слева от окна редактирования (Рисунок 2.8), на которой мнемоническими значками отображается отладочная информация
Невидимые компоненты
Не каждый компонент виден на форме во время запуска программы. Например, размещение на форме компонента MainMenu приводит к появлению в разра-батываемом приложении меню, но соответствующая пиктограмма во время запуска программы не отображается. Компоненты, представляющие диалоговые окна общего назначения, вообше никак не визуализируются во время работы программы. Размеры невидимого компонента в процессе разработки не изменяются — он всегда отображается в виде пиктограммы.
Объекты ActiveX в среде DAX
Ниже показана иерархия классов среды DAX, которые поддерживают объекты СОМ, типизированные объекты СОМ, серверы автоматизации, элементы управления ActiveX и формы ActiveForm.
TComObject
TTypedComObject
TAutoObject
TActiveXControl
TCustomForm
TActiveForm
Класс TCustomForm вместо класса TForm использован в качестве предка класса TActiveForm, так как в TCustomForm включены только свойства и методы, уместные для формы ActiveX.
Окно Call Stack
В этом окне представлен список всех функций и процедур, вызванных к моменту достижения точки выполнения и работа которых приостановлена. Для открытия этого окна, показанного на рис 2.19, используйте команду View/Call Stack. Рисунок 2.18. Использование окна Modules для вывода списка модулей, используемых приложением. Рисунок 2.19 Использование окна Call Stack для определения всех вызванных функций и процедур.
В верхней строке окна выводится имя текущей процедуры (DontHitMe в приведенном примере). В следующей строке указывается имя процедуры, вызвавшей данную процедуру (Ouch) и т.д. Это окно может быть очень полезным, когда надо определить, каким путем вы достигли точки останова. Дополнительные возможности предоставляет контекстное меню окна, которое можно вызвать, щелкнув правой кнопкой мыши.
Окно CPU (дизассемблер)
Окно CPU предоставляет возможность увидеть работу приложения на уровне языка ассемблера. Те, кто использовали отладчик Turbo Debugger for Windows, должны знать возможности, предоставляемые таким окном. Эффективное его использование предполагает знание ассемблера Intel x86 и архитектуры процессора, так что, если вы не вполне уверенно себя чувствуете, можете пропустить этот раздел.
Окно CPU требуется крайне редко, но если в нем возникла необходимость, значит, это действительно необходимость. Это — ultima ratio, последний довод, и используется он в безвыходных положениях, когда обычная трассировка кода не позволяет найти, понять и исправить ошибки. Только в таких случаях окно CPU и проход по ассемблерным инструкциям может приподнять завесу над причиной возникновения ошибок.
Чтобы использовать окно CPU необходимо его включить (по умолчанию оно отключено). Для этого придется использовать программу RegEdit из поставки Windows 95 (Windows NT). Запустите RegEdit и пробирайтесь по иерархическому дереву папок. Сначала откройте папку HKEY_CURRENT_USER. Во вложенной ветви зайдите в папку Software, затем — в Borland, Delphi и, наконец, в 4.
Одна из папок называется Debugging. Щелкните на ней и, когда она откроется, появится список пар Имя/Данные в окошке справа. Добавьте новое значение с именем EnableCPU и значением 1.
При следующем запуске Delphi вы увидите новый подпункт меню View/CPU Window.
Для вывода окна выберите View/CPU Window.
Перечислим панели Code pane Панель кода представляет дизассемблированный код в окрестности текущей точки выполнения (если вы не отлаживаете приложение, окно будет полупустым). Кроме того, панель показывает исходный текст строк, соответствующих выполняемому коду. В окне редактирования точка выполнения индицируется маленьким зеленым значком. При пошаговом проходе значок точки выполнения синхронно перемещается по окну CPU и окну редактирования. Register pane. В панели регистров отображается содержимое 16 регистров процессора. Значения регистров, изменившиеся в результате выполнения последней операции, выделены красным цветом. Flags pane. Панель флагов показывает состояние 14 флагов процессора. Установленный флаг представляется значением 1, сброшенный флаг значением 0. В зависимости от процессора некоторые флаги могут быть недоступными. Stack pane. Панель стека показывает содержимое стека приложения. Вы можете изменять представление содержимого стека с помощью контекстного меню. Data pane. По умолчанию в панели данных выводится содержимое глобального сегмента данных приложения. Ее вид можно изменить так же, как и вид панели стека. Каждая из панелей в окне CPU имеет собственное контекстное меню. Вы можете поэкспериментировать с пунктами меню и получить более полное представление о возможностях манипулирования панелями.
Окно Modules
В окне Modules отображаются все модели (ЕХЕ-файл вашего приложения и все используемые динамические библиотеки), которые располагаются в адресном пространстве приложения. В него входят непосредственно подключенные DLL и библиотеки, подключенные через другие библиотеки, а также библиотеки, загруженные операционной системой. Чтобы увидеть это окно, изображенное на рис 2.18. выберите команду View/Modules. В окне информация выводится в трех столбцах Name (имя модуля), Address (адрес начала кода модуля) и Path (полный путь каталога, из которого был загружен модуль). Информация о каталоге может быть важна, если возможна загрузка модуля не из того каталога, из которого ожидалась, например, в более старой версии. Информация об адресе обычно используется при отладке в окне CPU.
Окно состояния подзадач
Окна Thread Status, Modules и Call Stack предоставляют дополнительную информацию, которая может быть полезна при отладке приложения.
В окне Thread Status перечислены все активные подзадачи текущего процесса. Для просмотра состояния подзадач выберите команду View/Threads, и на экране появится окно Thread Status (рис 2.17).
Рис 2.17. Использование окна Thread Status для просмотра атрибутов подзадач в приложении
В четырех колонках окна представлена следующая информация: Thread ID. Уникальный идентификатор подзадачи, присвоенный ей операционной системой. State. Состояние подзадачи, обычно — Running или Stopped. Если ваше приложение запущено, но ожидает ввода от пользователя, состояние выводится как Runnable. Status. Статус подзадачи может иметь одно из четырех значений. Breakpoint означает, что поток остановлен в точке останова. Stepped— подзадача находится в режиме пошагового выполнения. Faulted— остановка подзадачи из-за исключительной ситуации и Unknown — статус неизвестен. Location. В этой колонке выводится строка исходного кода, соответствующего текущей точке выполнения подзадачи. Если отладчик не в состоянии определить строку исходного текста, выводится 32-битовый адрес точки выполнения. Если вами разработано приложение с несколькими подзадачами, и вы хотите отладить одну из подзадач, можете сделать ее основной с помощью окна Thread Status. Выберите подзадачу, которою вы хотите сделать текущей, и щелкните на ней правой кнопкой мыши. Выберите из контекстного меню команду Make Current. При этом фокус выполнения будет передан выбранной подзадаче, и вы сможете отлаживать ее как основную задачу.
В контекстном меню окна содержатся две команды — View Source и Go to Source. Они могут пригодиться для того, чтобы проследить за точкой выполнения другой подзадачи без передачи ей фокуса.
Отладка DLL
В предыдущих версиях Delphi для отладки библиотек динамической компоновки требовался внешний отладчик (Turbo Debugger for Windows). Delphi 4 внесла возможность отладки DLL в список своих возможностей. Windows не может загрузить DLL без предварительной загрузки использующего ее ЕХЕ, поэтому вам с начало придется набросать простенькую программку, использующую интересующую вас DLL. В главном меню выберите команду Run/Parameters для вывода диалогового окна Run Parameters. Если текущий проект— DLL (DPR-файл начинается ключевым словом library, а не program), поле Host Application будет доступно, и в нем вам надо либо ввести имя использующей DLL программы, либо выбрать его с помощью кнопки Browse.
После выбора приложения запуск и отладка DLL становятся обычной рутинной работой со всеми возможностями, используемыми при отладке программ, — установкой точек останова, просмотром значений переменных и т.д.
Точно так же вы будете отлаживать и свои компоненты ActiveX, и объекты автоматизации OLE.
Отладочная и коммерческая версии кода
Те, кто участвовали в "полевых испытаниях" (известных как бета-тестрирование) коммерческих программ, наверняка обратили внимание, что такие версии программ более медлительны, гораздо более "разговорчивы" и размером побольше окончательных версий программ. Может быть, разработчик спешил и выпустил "сырой" продукт, который будет улучшать перед выпуском окончательного варианта? Так тоже бывает, но главная причина в другом: в бета-версии содержится тестовый и отладочный коды, используемые разработчиком для проверки корректности работы программы.
Delphi позволяет очень легко внести тестовый и отладочный коды в приложение. Например, вы хотите создать приложение работы с базой данных и использовать быстрый, но, возможно, несколько рискованный алгоритм сортировки данных. Как же убедиться в корректности его работы? Один из путей — использовать в приложении два алгоритма одновременно (быстрый, но рискованный, и медленный, но проверенный), затем сравнить результаты работы обоих алгоритмов. Конечно же, этот вариант используется только в бета-версии, и после всестороннего тестирования, если все работает отлично и без сбоев, в конечной версии продукта останется только быстрый (и после такого тестирования — уже не рискованный) метод сортировки.
Для этого вам вовсе не надо использовать два разных текста программ — воспользуйтесь возможностью условного компилирования. Вы можете определить символ (я обычно использую Debug, но вы свободны в вашем выборе) для переключения между коммерческой и отладочной версиями вашего кода с использованием директив $IFDEF, $IFNDEF, $ELSE и $ENDIF. Вот пример использования "медленного" алгоритма в отладочной версии.
DataSet:= GetData; //Получение данных для сортировки.
{$ifdef Debug}
TestResultSet:= Sort_Tortoise(DataSet); //Медленно и надежно.
{$endif}
ResultSet:= Sort_Hare(DataSet); //Быстро и рискованно.
{$ifdef Debug}
if not CompareData(ResultSet, TestResultSet) then
//Результаты совпали?
Raise Exception.Create('Сортировка в DataSorting некорректна');
{$endif}
Если определен символ Debug, код принимает следующий вид.
DataSet:= GetData; //Получение данных для сортировки.
TestResultSet:= Sort_Tortoise(DataSet); //Медленно и надежно.
ResultSet:= Sort Hare(DataSet); //Быстро и рискованно.
if not CompareData(ResultSet, TestResultSet) then
//Результаты совпали?
Raise Exception.Create('Сортировка в DataSorting некорректна');
Если же символ Debug не определен при создании коммерческого варианта программы, код вырождается в алгоритм быстрой сортировки без дополнительных проверок
DataSet:= GetData; //Получение данных для сортировки.
Re5ultSet:= Sort_Hare(DataSet); //Быстро и рискованно.
Как видите, использование условной компиляции — простои способ создания как отладочной, так и коммерческой версий приложения Вы можете определить символ условной компиляции двумя путями. Первый — глобальное определение символа в опциях проекта. Выберите команду Project/Options и в диалоговом окне Project Options, во вкладке Directories/Conditionals, введите символ в поле Conditional defines. На рис 2.1 показано определение двух символов (Debug и Alpha) Щелкните на кнопке ОК для подтверждения вашего ввода
Совет: Изменив символы условной компиляции, перекомпилируйте проект с помощью команды Project/Build All для того, чтобы учесть внесенные изменения.
Другой метод определения символа условной компиляции — вставить в ваш исходный код директиву.
{$define Debug}
Вероятно, вы не захотите возиться с каждым файлом, входящим в проект, и предпочтете определить символ глобально. Однако возможна ситуация, когда, включив символ условной компиляции глобально, вы захотите отключить его в некоторых модулях. Для этого используйте в файле директиву
{$undef Debug}
Она отключает действие директивы Debug до тех пор, пока не встретится соответствующая директива $DEFINE или конец текущего файла. Конечно, вы можете использовать эти директивы сколь угодно часто и в тех местах, где сочтете нужным.
Помимо директив условной компиляции, есть еще немало других директив, которые могут использоваться в отладочной версии приложения. Я говорю "могут", поскольку эти директивы могут внести определенные различия в код коммерческой и тестовой версий, так что будьте осторожны при их применении. Эти опции перечислены во вкладке Compiler диалогового окна Project Options, приведенного на рис 2.2
Ниже приведено описание этих опций. Optimization. Эта опция управляет оптимизацией компилятора. Рекомендуется оставить эту опцию включенной и выключать ее, если вы полагаете, что оптимизация вносит ошибки в вашу программу. Управлять оптимизацией локально вы можете с помощью директив компилятора $0+ и $0-. Stack Frames. Если эта установка включена, компилятор всегда включает в функцию код для генерации кадра стека, даже если код не использует стек. Как и в случае оптимизации, вам вряд ли стоит изменять эту установку. Локальные директивы компилятора— $W-t и $W-. Range Checking. Проверка диапазона перехватывает ошибки, вызванные выходом за пределы массива или строки. Однако дополнительный код сдерживает выполнение программы и, по всей видимости, вы отключите эту опцию в коммерческой версии. Директивы компилятора для включения и отключения проверки— $R+ и $R-. Assertions (С). Эта опция более полно описана в следующем разделе. Использование данного типа проверок позволяет быстро и просто добавить проверки в код Естественно, в коммерческой версии вы захотите отключить эту возможность. Директивы компилятора— $С+ и $С-. Overflow checking (Q). Проверка на переполнение позволяет выяснить, не является ли результат выполнения целочисленной операции слишком большим для размещения его в переменной. Подобно опции Range Checking, данная опция полезна только при отладке, и в коммерческой версии, как правило, отключается. Директивы компилятора— $Q+ и $Q-. Отладочная версия вашего кода, вероятно, будет больше по размеру и медленнее коммерческой версии. Поэтому не передайте случайно конечному пользователю отладочную версию!
Packages
Существует огромное количество опции, которые можно изменять при разработке проекта. Они представлены на вкладках диалогового окна Project Options (см. Рисунок 1.14).
Вы всегда можете установить опции по умолчанию, отключив переключатель Default в нижнем левом углу
диалогового окна Project Options. заметку
Packages
Вкладка Packages позволяет определить, какие пакеты доступны для использования при разработке приложения, и прилинковать их при создании результирующего файла.
Группа Design packages предоставляет список зарегистрированных пакетов, которые можно выбрать для использования в приложении.
Группа Runtime packages дает возможность определить, какие пакеты компоновщик будет использовать при построении выходного файла. По умолчанию опция Build with runtime packages отключена, а это означает, что все объекты из VCL будут скомпонованы с вашим приложением. Включение опции означает, что ваше приложение будет разделять с другими приложениями Delphi одну копию пакетов.
Перечисления (Enum) в библиотеках типов
Вкладка Attributes в описании перечисления
Вкладка Members в описании перечисления
Перечисления (Enum) в библиотеках типов
Перечисления в библиотеках типов практически не отличаются от перечислений языка Object Pascal. Перечисление в библиотеке типов — это коллекция связанных констант, используемая как тип данных во всех остальных элементах библиотеки для определения свойств или методов. Кроме того, в зависимости от среды программирования константы перечисления могут использоваться с объектом ActiveX внутри среды программирования. В следующих двух разделах рассматривается назначение вкладок Attributes и Members в описании перечислений.
Вкладка Attributes в описании перечисления
На Рисунок 3.2 показано перечисление (Enum), выбранное в окне редактора библиотек типов Delphi с вкладкой Attributes в правой части окна редактора. В табл. 3.3 перечислены все возможные атрибуты, которые можно использовать с перечислением.
Перечислимые типы
Type enum type = (first value, value2, value3, last value);
Обычно данные перечислимых типов содержат дискретные значения, представляемые не числами, а именами. Тип Boolean— простейший перечислимый тип в Object Pascal. Булевы переменные могут принимать два значения, выражаемые именами True и False, а сам тип определен в Object Pascal так, как будто он объявлен следующим образом:
Type Boolean = (False, True);
С помощью типа Boolean в Object Pascal выполняются сравнения, большинство же перечислимых типов — это просто списки уникальных имен или идентификаторов, зарезервированных с конкретной целью. Например, можно создать тип MyColor (мой цвет) со значениями myRed, myGreen и myBlue (мой красный, мой зеленый, мой синий). Это делается совсем просто:
Type MyColor = (myRed, myGreen, myBlue);
В этой строке объявлены четыре новых идентификатора: MyColor, myRed, myGreen и myBlue. идентификатором MyColor обозначен порядковый тип, следовательно, в синтаксисе Object Pascal можно применять этот идентификатор везде, где разрешены перечислимые типы. Остальные три идентификатора— это значения типа MyColor. Подобно символьным и булевым типам перечислимые не являются числами, и использовать их наподобие чисел не имеет смысла. Однако перечислимые типы относятся к порядковым, так что значения любого такого типа упорядочены. Идентификаторам в списке присваиваются в качестве порядковых номеров последовательные числа. Первому имени присваивается порядковый номер 0, второму — 1 и т.д.
Совет: В С и C++ есть тип enema, аналогичный перечислимому типу Delphi. Но в этих языках можно произвольно присваивать идентификаторам постоянные значения. В Delphi же соответствие имен и их значений фиксиро-вано: первому имени присваивается значение 0, каждому последующему — на единицу больше. В С тип enum применяется лишь как средство быстрого определения набора целых постоянных. В C++ объявленные в перечислимом типе идентификаторы можно присваивать только переменным того же типа.
Поддиапазонные типы
Переменные поддиапазонного типа содержат информацию, соответствующую некоторому pаданному диапазону значений исходного типа, представляющего любой порядковый тип, кроме поддиапазонного. Синтаксис определения поддиапазонного типа имеет следующий вид:
Type subrange type = low value...high value;
Поддиапазонные переменные сохраняют все особенности исходного типа. Единственное отличие состоит в том, что переменной поддиапазонного типа можно присваивать только значения, входящие в заданный поддиапазон. Контроль за соблюдением этого условия задается командой проверки диапазона (range checking).
Необходимость явно определять поддиапазонный тип возникает нечасто, но все программисты неявно применяют эту конструкцию при определении массивов. Именно в форме поддиапазонной конструкции задается схема нумерации элементов массива.