Эта статья описывает новую возможность Delphi 2010 - визуализаторы для отладчика (Debugger Visualizers), а также подробно описывает, как вам создать ваш собственный визуализатор.
Что такое визуализатор для отладчика
Визуализатор для отладчика позволяет данным, показываемым в отладчике для конкретного типа данных, быть представленными в другом формате. К примеру, типTDateTime
при просмотре в Watch List или подсказке Evaluator Tooltip будет показан как простое число с плавающей точкой. В большинстве случае это значение вам не нужно. Тут на сцену и выходят визуализаторы для отладчика, которые показывают переменную типа TDateTime
(равно как и только TDate
или TTime
) в простом для чтения формате. Т.е. вместо значения '40105.900806'
вы видите '19.10.2009 21:37:09'
.Типы визуализаторов
Есть два типа визуализаторов. Основной тип - это Value-Replacer ("заменитель значения"). Визуализатор этого типа просто подменяет отображаемую строку на отформатированное значение выражения. Для этого типа визуализаторов есть ограничение: только один визуализатор этого типа может быть зарегистрирован для конкретного типа.Более сложным вариантом является визуализатор типа External-Viewer ("внешний просмотрщик"). Визуализатор этого типа позволяет пользователю показать внешнее окно для отображения дополнительной информации или расширенного GUI для выбранного типа. На визуализаторы этого типа ограничений нет: вы можете зарегистрировать их сколько угодно на один тип.
Встроенные визуализаторы
Delphi 2010 поставляется с двумя встроенными визуализаторами для отладчика (по-умолчанию они включены). ВизуализаторTDateTime
показывает отформатированное значение "на месте". Это означает, что теперь там, где раньше везде показывалось число с плавающей запятой - теперь будет нормальное отображение даты и времени. Вам не нужно делать никаких дополнительных телодвижений, чтобы активировать этот визуализатор. Визуализатор TDateTime
- это пример визуализатора типа Value-Replacer. Визуализатор для TStrings
- это пример визуализатора типа External-Viewer. Этот визуализатор показывает всплывающее окно, которое показывает содержимое любого значения типа TStrings
.Вызов визуализатора типа External-Viewer
Визуализаторы с внешним просмотрщиком показываются в IDE несколько иначе, чтобы подсказать, что переменная может быть просмотрена в отдельном окне. Следующие примеры показывают интерфейс пользователя для внешнего визуализатора для Watch List и Evaluator Tooltip:Щелчок по иконке с раскрывающей стрелкой вниз покажет вам меню со всеми визуализаторами, ассоциированными с переменной этого типа. Да, это так: у вас может быть несколько визуализаторов типа External-Viewer.
Где можно использовать визуализаторы для отладчика?
Визуализаторы доступны во многих местах в IDE. Они работают в таких отладочных окнах как:- Watch List
- Local Variables
- Debug Inspector
- Evaluate / Modify
- Evaluator Tooltips
Отключение визуализаторов
Если вы хотите увидеть представление переменной по-умолчанию (без визуализаторов), то у вас есть три способа отключения визуализаторов для конкретного типа:- Откройте свойства Watch-а и сбросьте флажок 'Use visualizer':
- Отключите визуализатор в списке доступных визуализаторов для отладчика (Tools | Options | Debugger Options | Visualizers).
- Сделайте приведение типов для Watch-а. Для примера выше это будет
Double(FDateTime)
. Примечание: это работает не всегда.
Создание своих собственных визуализаторов для отладчика
Надеюсь, что теперь вы имеете представление о том, что такое визуализаторы для отладчика и как они могут упростить отладку. Теперь мы создадим два визуализатора разных типов для типаTColor
. Мы начнём с базовых вещей для тех, кто никогда раньше не писал IDE-экспертов.Написание IDE эксперта - обзор
Для экспертов IDE у вас есть два варианта: пакеты или DLL. Оба варианта имеют свою плюсы и минусы. Пакеты включаются мгновенно: вы устанавливаете их в IDE и они работают сразу же. Вы можете тестировать их во время разработки. DLL же требуют от вас перезапуска IDE (и ручного обновления реестра) или же запуск второй копии IDE для отладки эксперта. В нашей статье визуализаторы для отладчика будут располагаться в пакете.Примечание: вам следует быть в курсе, что имена пакетов, используемые в IDE, должны быть уникальными среди всех загруженных пакетов.
Настройка группы проектов
Запустите Delphi 2010 и создайте новый проект пакета. Сохраните проект в новой папке с уникальным именем. Я назвал проектColorVisualizer2010
.Примечание: я не использую свойства Suffix или Prefix (которые в Delphi 2010 называются “Shared object name”).
Сначала мы создадим визуализатор типа Value-Replacer. Для этого добавьте новый модуль в проект пакета и сохраните его как
CVAddin.pas
. Также добавьте в пакет новый frame и сохраните его как CVViewerFrame.pas
. Щёлкните правой кнопкой по узлу Requires нашего пакета в менеджере проектов и выберите команду "Add Reference...". В поле Package Name напечатайте “designide” и нажмите OK. Это пакет, который даёт нам функциональность для IDE эксперта. Сославшись на него, мы теперь можем использовать различные интерфейсы Open Tools API.Добавьте новый проект в группу проектов. На этот раз добавьте VCL Forms Application - это будет приложение, которое мы будем отлаживать с целью проверки работы наших визуализаторов. Сохраните проект как
VisualizerTestProj
. Сохраните его главный модуль как MainForm
. И, наконец, сохраните всю группу проектов как ColorVisualizer2010Group
.Снимок экрана ниже показывает, как должен выглядеть вам менеджер проекта к этому моменту:
Регистрация визуализатора для отладчика
Open Tools API построено на интерфейсах. Практически любая функциональность требует от вас регистрации класса, который реализует конкретные интерфейсы.Чтобы визуализатор мог работать, он должен быть зарегистрирован в IDE. Это делается с помощью вызова метода
RegisterDebugVisualizer
интерфейса IOTADebuggerServices
. Метод принимает параметр интерфейсного типа IOTADebuggerVisualizer
. Это означает, что мы должны создать класс, который реализует этот интерфейс. Вероятно, вы захотите наследовать класс от TInterfacedObject
. Ну а поскольку мы собираемся реализовывать ещё и интерфейс IOTADebuggerVisualizer
, то нам также необходимо реализовать методы этого интерфейса.
type TColorVisualizer = class(TInterfacedObject, IOTADebuggerVisualizer) procedure GetSupportedType(Index: Integer; var TypeName: String; var AllDescendents: Boolean); function GetSupportedTypeCount: Integer; function GetVisualizerDescription: String; function GetVisualizerIdentifier: String; function GetVisualizerName: String; end;Этот класс должен возвращать такие данные:
GetVisualizerName
– используется для получения имени визуализатора в диалогах настроек.GetVisualizerDescription
– описание визуализатора в нём же.GetVisualizerIdentifier
– возвращает уникальный идентификатор визуализатора. Вероятно, строковое представление GUID будет неплохим выбором для этого поля. Если же вы используете читабельную строку - то добавьте к ней хотя бы префикс вашей компании.GetSupportedTypeCount
– возвращает число поддерживаемых вашим визуализатором типов.GetSupportedType
– этот метод вызывается GetSupportedTypeCount
число раз для получения информации о поддерживаемых вами типах. Параметр Index
указывает индекс итерации (от 0 до GetSupportedTypeCount - 1
). Для каждого вызова вы должны вернуть имя поддерживаемого вами типа в параметре TypeName
. В нашем примере у нас будет только один тип: 'TColor'
.Примечание: параметр
AllDescendents
игнорируется в Delphi 2010 для визуализаторов, поэтому вы должны всегда ставить его в False
. Если вы хотите зарегистрировать визуализатор для типа-класса и всех его подклассов - вам придётся регистрировать визуализатор для каждого дочернего класса.Хотя мы создали класс, реализующий интерфейс
IOTADebuggerVisualizer
, а его регистрация даже покажет визуализатор в спике доступных визуализаторов для отладчика, но реально он не будет работать, потому что для этого нам нужно реализовать ещё как минимум один интерфейс: IOTADebuggerVisualizerValueReplacer
или IOTADebuggerVisualizerExternalViewer
.Регистрация визуализатора
Прежде чем мы будем реализовывать эти интерфейсы, давайте сначала зарегистрируем наш наполовину готовый визуализатор.Когда IDE загружает пакет, она проверяет каждый модуль в пакете на наличие процедуры с именем
Register
(имя чувствительно к регистру). Если таковая находится, то IDE вызывает её. Обычно в этой процедуре пакеты размещают код регистрации компонент, экспертов и других IDE-вещей.Код ниже показывает регистрацию визуализатора:
var _Color: IOTADebuggerVisualizer; procedure Register; var LServices: IOTADebuggerServices; begin if Supports(BorlandIDEServices, IOTADebuggerServices, LServices) then begin _Color := TColorVisualizer.Create; LServices.RegisterDebugVisualizer(_Color); end; end; procedure RemoveVisualizer; var LServices: IOTADebuggerServices; begin if Supports(BorlandIDEServices, IOTADebuggerServices, LServices) then LServices.UnregisterDebugVisualizer(_Color); _Color := nil; end; initialization finalization RemoveVisualizer; end.Когда вы пишете код, который будет работать в IDE, я рекомендую вам использовать защитное программирование и добавлять побольше проверок. Помните: один элементарный баг может привести к вылету среды - и вы точно этого не хотите.
Вместо использования
Supports
на глобальной переменной BorlandIDEServices
(объявленной в модуле ToolsAPI), мы могли бы использовать конструкцию as
, чтобы получить тот же результат. Ну или почти тот же. Потому что использование Supports
не генерирует исключения, если BorlandIDEServices
не реализует интерфейс IOTADebuggerServices
.Реализация рабочих интерфейсов визуализатора
Имея два различные типа визуализаторов для отладчика - вы будете правы, если предположите, что нам надо реализовать два различных интерфейса.Value-Replacer
Чтобы создать визуализатор типа Value-Replacer, вам нужно реализовать интерфейсIOTADebuggerVisualizerValueReplacer
:
type IOTADebuggerVisualizerValueReplacer = interface(IOTADebuggerVisualizer) ['{6BBFB765-E76F-449D-B059-A794FA06F917}'] function GetReplacementValue(const Expression, TypeName, EvalResult: String): String; end;Добавьте интерфейс и метод
GetReplacementValue
в наш класс. Сейчас он должен выглядеть примерно так:
type TColorVisualizer = class(TInterfacedObject, TOTADebuggerVisualizer, IOTADebuggerVisualizerValueReplacer) function GetReplacementValue(const Expression, TypeName, EvalResult: String): String; procedure GetSupportedType(Index: Integer; var TypeName: String; var AllDescendents: Boolean); function GetSupportedTypeCount: Integer; function GetVisualizerDescription: String; function GetVisualizerIdentifier: String; function GetVisualizerName: String; end;Для нашего визуализатора-примера реализация метода
GetReplacementValue
будет:
function TColorVisualizer.GetReplacementValue(const Expression, TypeName, EvalResult: String): String; begin Result := ColorToString(StrToInt(EvalResult)); end;
Expression
- это имя переменной.TypeName
- имя типа параметра выражения.EvalResult
- представление результата выражения по-умолчанию.Функция
ColorToString
объявлена в модуле Graphics.pas
. Она возвращает строковое представление некоторых стандартных цветов VCL и Windows - типа clNavy и clBtnFace. Если цвет не является одним из стандартных, то возвращается HEX-представление цвета.External-Viewer
Чтобы создать визуализатор типа External-Viewer, реализуйте интерфейсIOTADebuggerVisualizerExternalViewer
:
type IOTADebuggerVisualizerExternalViewer = interface(IOTADebuggerVisualizer) function GetMenuText: String; function Show(const Expression, TypeName, EvalResult: String; SuggestedLeft, SuggestedTop: Integer): IOTADebuggerVisualizerExternalViewerUpdater; end;Первое, что вам нужно заметить в этом объявлении - что этот интерфейс возвращает новый интерфейс. Да, это потому что визуализаторы типа External-Viewer более сложны, чем Value-Replacer.
Нам нужно реализовать методы:
GetMenuText
– вспомните, что визуализаторы типа External-Viewer вызываются из всплывающего меню. Этот метод должен вернуть заголовок элемента меню для вызова нашего визуализатора.Show
– IDE вызывает этот метод, когда пользователь щёлкает по нашему пункту меню.Параметры
Expression
, TypeName
и EvalResult
имеют тот же смысл, что и в методе GetReplacementValue
интерфейса IOTADebuggerVisualizerValueReplacer
. SuggestedLeft
и SuggestedTop
указывают рекомендуемое положение на экране для окна визуализатора.Ваш метод показа визуализатора должен создать окно, которое показывает выражение и вернуть интерфейс
IOTADebuggerVisualizerExternalViewerUpdater
. Это означает, что вы должны создать класс, который будет его (интерфейс) реализовывать. В нашем примере для простоты интерфейс будет реализован фреймом:
type IOTADebuggerVisualizerExternalViewerUpdater = interface procedure CloseVisualizer; procedure MarkUnavailable(Reason: TOTAVisualizerUnavailableReason); procedure RefreshVisualizer(const Expression, TypeName, EvalResult: String); procedure SetClosedCallback(ClosedProc: TOTAVisualizerClosedProcedure); end;
CloseVisualizer
– когда вызывается этот метод, вам нужно закрыть ваше окно. Это означает, что поток, ответственный за ваш вызов, завершился.MarkUnavailable
– этот метод вызывается, когда значение становится недоступным. Сейчас это может быть из-за “Out of scope” или “Process not accessible”.RefreshVisualizer
– этот метод вызывается, когда значение нуждается в обновлении. Он вызывается, когда пользователь использует диалог Evaluate/Modify для изменения значения, которое показывает ваш визуализатор. Если визуализатор невидим, то он ничего и не показывает.SetClosedCallback
– устанавливает функцию обратного вызова (параметр ClosedProc
), которую вы должны вызывать, когда ваше окно закрывается, так что IDE может перестать вызывать RefreshVisualizer
. Это означает, что вам надо сохранить этот параметр для дальнейшего использования.Ниже показана реализация метода
Show
. Этот код вызывает классовый метод фрейма. Я специально скрыл реализацию метода, потому что она не имеет отношения к статье. Всё, что вам нужно знать, - что IDE фрейм, спроектированный для визуализатора, является дочерним контролом dockable-формы IDE.
function TColorVisualizer.Show(const Expression, TypeName, EvalResult: String; SuggestedLeft, SuggestedTop: Integer): IOTADebuggerVisualizerExternalViewerUpdater; begin Result := TfmCVViewer.CreateAndShow(Expression, TypeName, EvalResult, SuggestedLeft, SuggestedTop); end;
Вызов методов для выражений
Когда вы создаёте визуализатор типа External-Viewer, вы можете захотеть вызывать методы для выражений, которые вы показываете. Например, у вас может быть визуализатор TTable, и вы хотите показать содержимое текущей записи и количество записей всего. Вы можете сделать это, вызывая методы TTable - для этого есть специальная техника, которую я не буду описывать здесь. Она будет описана в следующем выпуске журнала.Поддержка C++
Сначала я должен сказать, что я НЕ являюсь разработчиком C++.Чтобы поддерживать C++ в визуализаторе, вам нужно изменить две вещи. Во-первых, вам нужно возвращать
2
как число поддерживаемых визуализатором типов данных, а также возвращать полное имя типа в параметре TypeName
метода GetSupportedType
. Оба этих изменения были сделаны в исходном коде, прилагающемся к статье. Благодарю тех двух разработчиков C++, которые указали на этот момент.Создание интерфейса пользователя для визуализатора
Фрейм, который мы добавили в проект - вот то, что будет видеть пользователь. Мы сделаем в нём самый примитивный пользовательский интерфейс - панель, меню и action list. Цвет фрейма будет показывать цвет выражения, а имя/значение цвета будет показано на панели. Action list и меню будут содержать действия для копирования выражения и результата вычисления в буфер обмена.Снимок экрана ниже показывает фрейм в режиме проектирования:
Установка визуализатора
Чтобы установить визуализатор, щёлкните правой кнопкой мыши на менеджере проектов и выберите команду "Install". После установки вы можете найти наш визуализатор в секции Debugger Options | Visualizers диалога Tools | Options dialog.Готовый визуализатор в работе
Осталось показать, как визуализатор работает в IDE.Запустите тестовый проект, прикладываемый к примеру - это простое приложение с одной формой и тремя кнопками:
Каждая кнопка меняет свойство
Color
формы.Установите точки останова на обработчики событий всех кнопок и нажмите на первую кнопку. После остановки активируйте визуализатор, выбором команды "Show Color" из меню визуализаторов:
На фоне вы также можете видеть работу визуализатора Value-Replace, который показывает clBtnFace.
После нажатия же на "Show Color" вы увидите окно визуализатора типа External-Viewer:
Если вы выберите пользовательский цвет по третьей кнопке, который не совпадает со стандартными цветами, то визуализатор будет выглядеть примерно так:
Заключение
У меня нет сомнений, что визуализаторы для отладчика упростят ваши сессии отладки. Это особенно так, когда вам нужно закапываться и вызывать методы для визуализируемого выражения (напомню, эта тема следующей статьи).Исходный код и тестовый проект доступны для всех подписчиков журнала Blaise Pascal. Не забудьте прочитать следующий номер с продолжением этой статьи.
Рульная вещь, ни хрена про неё не знал, хоть уже и полгода на ксе. Все бахал шоумессаджес и стринглисты в файл...)))
ОтветитьУдалитьА зачем было переводить неполную статью?
ОтветитьУдалить