Создание пользовательских представлений для объектов C++ в отладчике с помощью платформы Natvis
Платформа Visual Studio Natvis позволяет настраивать способ отображения собственных типов в окнах переменных отладчика, таких как Локальные и Контрольные значения, а также DataTips. Визуализации Natvis улучшают отображение создаваемых типов во время отладки.
Natvis заменяет файл autoexp.dat в предыдущих версиях Visual Studio синтаксисом XML и обеспечивает улучшенную диагностику, управление версиями и поддержку нескольких файлов.
Примечание.
Настройки Natvis работают с классами и структурами, но не с определениями типов.
Визуализации Natvis
Платформу Natvis можно использовать для создания правил визуализации для создаваемых типов, чтобы разработчики могли легко получить к ним доступ во время отладки.
Например, на следующем рисунке показана переменная типа Windows::UI::XAML::Controls::TextBox в окне отладчика без применения пользовательских визуализаций.
В выделенной строке показано свойство Text
класса TextBox
. Поиск этого свойства затруднен из-за сложной иерархии классов. Отладчик не знает, как интерпретировать тип пользовательской строки, поэтому вы не видите строку, находящуюся внутри текстового поля.
Тот же тип объекта TextBox
выглядит гораздо проще в окне переменных, когда применяются пользовательские правила визуализации Natvis. Важные члены класса отображаются вместе, и отладчик может показывать базовое строковое значение типа настраиваемой строки.
Использование NATVIS-файлов в проектах C++
Natvis использует NATVIS-файлы для указания правил визуализации. NATVIS-файл — это XML-файл с расширением NATVIS. Схема Natvis определена в папке <папка установки VS>\Xml\Schemas\1033\natvis.xsd.
Основной структурой NATVIS-файла является один элемент Type
или несколько, представляющих записи визуализации. Полное имя каждого элемента Type
задается в его атрибуте Name
.
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="MyNamespace::CFoo">
.
.
</Type>
<Type Name="...">
.
.
</Type>
</AutoVisualizer>
В Visual Studio некоторые NATVIS-файлы доступны в папке <папка установки VS>\Common7\Packages\Debugger\Visualizers. Эти файлы имеют правила визуализации для многих распространенных типов и могут служить примерами для написания визуализаций для новых типов.
Добавление NATVIS-файла в проект C++
Вы можете добавить NATVIS-файл в любой проект на языке C++.
Добавление нового NATVIS-файла
Выберите узел проекта C++ в обозревателе решений, выберите Проект>Добавить новый элемент или щелкните проект правой кнопкой мыши и выберите Добавить>Новый элемент.
Если вы не видите все шаблоны элементов, выберите "Показать все шаблоны".
В диалоговом окне Добавление нового элемента последовательно выберите Visual C++>Служебная программа>Файл визуализации отладчика (NATVIS).
Присвойте файлу имя и нажмите кнопку Добавить.
Новый файл добавляется в обозреватель решений и открывается в области документа Visual Studio.
Отладчик Visual Studio автоматически загружает NATVIS-файлы в проекты C++ и по умолчанию включает их в PDB-файл при сборке проекта. При отладке приложения сборки отладчик загружает NATVIS-файл из PDB-файла, даже если проект не открыт. Если вам не нужен NATVIS-файл, включенный в PDB-файл, его можно исключить из созданного PDB-файла.
Исключение NATVIS-файла из PDB-файла
В обозревателе решений выберите NATVIS-файл и щелкните значок Свойства либо щелкните файл правой кнопкой мыши и выберите пункт Свойства.
Раскрывающийся список со стрелкой рядом с "Исключенные из сборки " и нажмите кнопку "Да", а затем нажмите кнопку "ОК".
Примечание.
Для отладки исполняемых проектов используйте элементы решений в целях добавления любых NATVIS-файлов, которые отсутствуют в PDB-файле, так как нет доступных проектов C++.
Примечание.
Правила Natvis, которые загружаются из PDB-файла, применяются только к типам в модуле, к которому относится PDB-файл. Например, если в файле Module1.pdb есть запись Natvis для типа с именем Test
, эта запись применяется только к классу Test
в библиотеке Module1.dll. Если в другом модуле также определен класс с именем Test
, запись Natvis в Module1.pdb к нему не применяется.
Установка и регистрация NATVIS-файла с помощью пакета VSIX
С помощью пакета VSIX можно устанавливать и регистрировать NATVIS-файлы. Независимо от того, где они установлены, все зарегистрированные файлы .natvis автоматически собираются во время отладки.
Включите NATVIS-файл в пакет VSIX. Например, для следующего файла проекта:
<?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="14.0"> <ItemGroup> <VSIXSourceItem Include="Visualizer.natvis" /> </ItemGroup> </Project>
Зарегистрируйте NATVIS-файл в файле source.extension.vsixmanifest.
<?xml version="1.0" encoding="utf-8"?> <PackageManifest Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011" xmlns:d="http://schemas.microsoft.com/developer/vsx-schema-design/2011"> <Assets> <Asset Type="NativeVisualizer" Path="Visualizer.natvis" /> </Assets> </PackageManifest>
Расположения файлов Natvis
Если NATVIS-файлы необходимо применить к нескольким проектам, их можно добавить в каталог пользователя или системный каталог.
NATVIS-файлы определяются в следующем порядке.
Любые NATVIS-файлы, встроенные в отлаживаемый PDB-файл (если файл с тем же именем не существует загруженном проекте).
Любые NATVIS-файлы, являющиеся частью загруженного проекта C++ или решения верхнего уровня. В эту группу входят все загруженные проекты C++, включая библиотеки классов, но не проекты на других языках.
Любые NATVIS-файлы, установленные и зарегистрированные с помощью пакета VSIX.
- Каталог Natvis конкретного пользователя (например, %USERPROFILE%\Documents\Visual Studio 2022\Visualizers).
- Каталог Natvis конкретного пользователя (например, %USERPROFILE%\Documents\Visual Studio 2019\Visualizers).
- Каталог Natvis на уровне системы (<папка> установки Microsoft Visual Studio\Common7\Packages\Debugger\Visualizers). В этом каталоге находятся NATVIS-файлы, которые устанавливаются вместе с Visual Studio. При наличии прав администратора в этот каталог можно добавить другие файлы.
Изменение NATVIS-файлов во время отладки
Вы можете изменить NATVIS-файл в IDE во время отладки проекта. Откройте файл в том же экземпляре Visual Studio, с помощью которого выполняется отладка, измените и сохраните его. Сразу после сохранения файла окна Контрольные значения и Локальные должны обновиться в соответствии с изменениями.
Вы также можете добавить или удалить NATVIS-файлы в отлаживаемом решении, и Visual Studio добавит или удалит соответствующие визуализации.
Вы не можете обновить NATVIS-файлы, внедренные в PDB-файлы, во время отладки.
Если вы измените NATVIS-файл вне Visual Studio, изменения не вступят в силу автоматически. Для обновления окон отладчика можно заново выполнить команду .natvisreload в окне Интерпретация. В результате изменения вступят в силу без перезапуска сеанса отладки.
Кроме того, команда .natvisreload позволяет обновить NATVIS-файл до более новой версии. Например, NATVIS-файл может быть проверка в системе управления версиями, и вы хотите забрать последние изменения, внесенные кем-либо другим.
Выражения и форматирование
Визуализации Natvis используют выражения C++ для указания элементов данных для отображения. В дополнение к усовершенствованиям и ограничениям выражений C++ в отладчике, описанных в статье Оператор контекста (C++), необходимо учитывать следующие различия.
Выражения Natvis вычисляются в контексте визуализируемого объекта, а не текущего кадра стека. Например,
x
в выражении Natvis ссылается на поле с именем x в визуализируемом объекте, а не на локальную переменную с именем x в текущей функции. Локальные переменные недоступны в выражениях Natvis, хотя доступны глобальные переменные.Выражения Natvis не позволяют выполнять вычисление функции или реализовать побочные эффекты. Вызовы функций и операторы присваивания игнорируются. Так как встроенные функции отладчика являются побочными эффектами, они могут быть свободно вызваны из любого выражения Natvis, даже если другие вызовы функций запрещены.
Для управления отображением выражения можно использовать любой из описателей формата, описанных в статье Описатели формата в C++. Описатели формата игнорируются, если запись используется внутренне Natvis, например
Size
выражение в расширении ArrayItems.
Примечание.
Так как документ Natvis является XML, выражения не могут напрямую использовать амперсанд, больше, чем, меньше или сдвиг операторов. Эти символы должны быть экранированы как в теле элемента, так и в операторах условия. Например:
\<Item Name="HiByte"\>(byte)(_flags \>\> 24),x\</Item\>
\<Item Name="HiByteStatus" Condition="(_flags \& 0xFF000000) == 0"\>"None"\</Item\>
\<Item Name="HiByteStatus" Condition="(_flags \& 0xFF000000) != 0"\>"Some"\</Item\>
Представления Natvis
Можно определить различные представления Natvis для отображения типов различными способами. Например, ниже приведена визуализация std::vector
, которая определяет упрощенное представление с именем simple
. Элементы DisplayString
и ArrayItems
отображаются в представлении по умолчанию и представлении simple
, а элементы [size]
и [capacity]
не отображаются в представлении simple
.
<Type Name="std::vector<*>">
<DisplayString>{{ size={_Mylast - _Myfirst} }}</DisplayString>
<Expand>
<Item Name="[size]" ExcludeView="simple">_Mylast - _Myfirst</Item>
<Item Name="[capacity]" ExcludeView="simple">_Myend - _Myfirst</Item>
<ArrayItems>
<Size>_Mylast - _Myfirst</Size>
<ValuePointer>_Myfirst</ValuePointer>
</ArrayItems>
</Expand>
</Type>
В окне Контрольные значения можно использовать описатель формата ,view для указания альтернативного представления. Простое представление отображается как vec,view(simple).
Ошибки Natvis
Когда отладчик обнаруживает ошибки в записи визуализации, он пропускает их. Он либо отображает тип в его необработанном виде, либо выбирает другую подходящую визуализацию. С помощью диагностики Natvis можно понять, почему отладчик проигнорировал запись визуализации, и просмотреть базовые ошибки синтаксиса и анализа.
Включение диагностики Natvis
- Последовательно выберите Сервис>Параметры (или Отладка>Параметры) >Отладка>Окно вывода, задайте для параметра Диагностические сообщения Natvis (только C++) значение Ошибка, Предупреждение или Подробно, а затем нажмите кнопку OK.
Ошибки появляются в окне Вывод.
Справочник по синтаксису Natvis
В файле Natvis можно использовать следующие элементы и атрибуты.
Элемент AutoVisualizer
Элемент AutoVisualizer
— это корневой узел NATVIS-файла, который содержит атрибут xmlns:
пространства имен.
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
.
.
</AutoVisualizer>
Элемент AutoVisualizer
может иметь дочерние элементы Type, HResult, UIVisualizer и CustomVisualizer.
Элемент Type
Базовый элемент Type
выглядит следующим образом.
<Type Name="[fully qualified type name]">
<DisplayString Condition="[Boolean expression]">[Display value]</DisplayString>
<Expand>
...
</Expand>
</Type>
Элемент Type
определяет следующее.
Для какого типа должна использоваться эта визуализация (атрибут
Name
).Как должно выглядеть значение объекта этого типа (элемент
DisplayString
).Как должны выглядеть члены типа, когда пользователь раскрывает тип в окне переменной (узел
Expand
).
Шаблонные классы
Атрибут Name
элемента Type
принимает символ звездочки *
как подстановочный знак, который можно использовать для имен шаблонных классов.
В следующем примере используется та же визуализация, независимо от того, представляет ли объект собой CAtlArray<int>
или CAtlArray<float>
. Если есть специальная запись визуализации для CAtlArray<float>
, она имеет приоритет над универсальной.
<Type Name="ATL::CAtlArray<*>">
<DisplayString>{{Count = {m_nSize}}}</DisplayString>
</Type>
На параметры шаблона можно ссылаться в записи визуализации с помощью макросов $T1, $T2 и т. д. Чтобы найти примеры этих макросов, см. NATVIS-файлы, поставляемые в комплекте с Visual Studio.
Сопоставление типов визуализатора
Если запись визуализации не удается подтвердить, используется следующая доступная визуализация.
Атрибут Inheritable
С помощью необязательного атрибута Inheritable
можно указать, применяется ли визуализация только к базовому типу или к базовому типу и всем производным типам. Значение Inheritable
по умолчанию — true
.
В следующем примере визуализация применяется только к типу BaseClass
.
<Type Name="Namespace::BaseClass" Inheritable="false">
<DisplayString>{{Count = {m_nSize}}}</DisplayString>
</Type>
Атрибут Priority
Необязательный атрибут Priority
задает порядок, в котором следует использовать альтернативные определения, если не удается выполнить синтаксический анализ определения. Допустимые значения Priority
— Low
, MediumLow
, Medium
, MediumHigh
и High
. Значение по умолчанию — Medium
. Атрибут Priority
различает приоритеты только в одном NATVIS-файле.
В следующем примере сначала анализируется запись, соответствующая 2015 STL. Если анализ завершается ошибкой, используется альтернативная запись для версии 2013 библиотеки STL.
<!-- VC 2013 -->
<Type Name="std::reference_wrapper<*>" Priority="MediumLow">
<DisplayString>{_Callee}</DisplayString>
<Expand>
<ExpandedItem>_Callee</ExpandedItem>
</Expand>
</Type>
<!-- VC 2015 -->
<Type Name="std::reference_wrapper<*>">
<DisplayString>{*_Ptr}</DisplayString>
<Expand>
<Item Name="[ptr]">_Ptr</Item>
</Expand>
</Type>
Атрибут Optional
Атрибут Optional
можно разместить на любом узле. Если не удается проанализировать часть выражения внутри необязательного узла, отладчик игнорирует этот узел, но применяет остальные правила Type
. В следующем типе [State]
является обязательным, а [Exception]
— необязательным. Если у MyNamespace::MyClass
есть поле с именем _M_exceptionHolder
, отображаются оба узла — [State]
и [Exception]
, но, если поле _M_exceptionHolder
отсутствует, отображается только узел [State]
.
<Type Name="MyNamespace::MyClass">
<Expand>
<Item Name="[State]">_M_State</Item>
<Item Name="[Exception]" Optional="true">_M_exceptionHolder</Item>
</Expand>
</Type>
Атрибут Condition
Необязательный атрибут Condition
доступен для многих элементов визуализации и определяет, когда следует использовать правило визуализации. Если выражение внутри атрибута условия разрешается в false
, правило визуализации не применяется. Если оно разрешается в true
или атрибут Condition
отсутствует, визуализация применяется. Этот атрибут можно использовать для логики if-else в записях визуализации.
Например, следующая визуализация содержит два элемента DisplayString
для типа интеллектуального указателя. Если первый член _Myptr
пуст, условие первого элемента DisplayString
разрешается в true
и отображается форма. Если элемент _Myptr
не пуст, условие вычисляется как false
, и отображается второй элемент DisplayString
.
<Type Name="std::auto_ptr<*>">
<DisplayString Condition="_Myptr == 0">empty</DisplayString>
<DisplayString>auto_ptr {*_Myptr}</DisplayString>
<Expand>
<ExpandedItem>_Myptr</ExpandedItem>
</Expand>
</Type>
Атрибут IncludeView и ExcludeView
Атрибуты IncludeView
и ExcludeView
указывают элементы, которые должны отображаться или не отображаться в конкретных представлениях. Например, в следующей спецификации Natvis std::vector
в представлении simple
не отображаются элементы [size]
и [capacity]
.
<Type Name="std::vector<*>">
<DisplayString>{{ size={_Mylast - _Myfirst} }}</DisplayString>
<Expand>
<Item Name="[size]" ExcludeView="simple">_Mylast - _Myfirst</Item>
<Item Name="[capacity]" ExcludeView="simple">_Myend - _Myfirst</Item>
<ArrayItems>
<Size>_Mylast - _Myfirst</Size>
<ValuePointer>_Myfirst</ValuePointer>
</ArrayItems>
</Expand>
</Type>
Атрибуты IncludeView
и ExcludeView
можно использовать для типов, а также для отдельных членов.
Version, элемент
Элемент Version
ограничивает запись визуализации конкретным модулем и версией. Элемент Version
помогает избежать конфликтов имен, сокращает число непреднамеренных несоответствий и позволяет использовать различные визуализации для разных версий типов.
Если общий файл заголовка, используемый различными модулями, определяет тип, версионная визуализация отображается только в том случае, когда тип находится в указанной версии модуля.
В следующем примере визуализация применима только для типа DirectUI::Border
, входящего в Windows.UI.Xaml.dll
в версиях с 1.0 по 1.5.
<Type Name="DirectUI::Border">
<Version Name="Windows.UI.Xaml.dll" Min="1.0" Max="1.5"/>
<DisplayString>{{Name = {*(m_pDO->m_pstrName)}}}</DisplayString>
<Expand>
<ExpandedItem>*(CBorder*)(m_pDO)</ExpandedItem>
</Expand>
</Type>
Требуется только один из атрибутов: Min
или Max
. Это необязательные атрибуты. Подстановочные знаки не поддерживаются.
Атрибут Name
имеет формат filename.ext, например Hello.exe или some.dll. Имена путей не допускаются.
Элемент DisplayString
Элемент DisplayString
указывает строку, которая будет отображаться как значение переменной. Принимает произвольные строки в сочетании с выражениями. Весь код внутри фигурных скобок интерпретируется как выражение. Например, запись DisplayString
<Type Name="CPoint">
<DisplayString>{{x={x} y={y}}}</DisplayString>
</Type>
означает, что переменные типа CPoint
отображаются, как показано на рисунке ниже.
В выражении DisplayString
x
и y
, которые являются членами CPoint
, находятся внутри фигурных скобок, и поэтому их значения вычисляются. В примере также показано, как можно экранировать фигурную скобку путем использования двойных фигурных скобок ({{
или }}
).
Примечание.
Элемент DisplayString
является единственным элементом, который принимает произвольные строки и синтаксис фигурных скобок. Все остальные элементы визуализации принимают только выражения, которые может вычислять отладчик.
Элемент StringView
Элемент StringView
определяет значение, которое отладчик может отправить во встроенный визуализатор текста. Например, предположим, что имеется следующая визуализация для типа ATL::CStringT
.
<Type Name="ATL::CStringT<wchar_t,*>">
<DisplayString>{m_pszData,su}</DisplayString>
</Type>
Объект CStringT
отображается в окне переменных, как в следующем примере.
Добавление элемента StringView
сообщает отладчику, что он может отображать значение как визуализацию текста.
<Type Name="ATL::CStringT<wchar_t,*>">
<DisplayString>{m_pszData,su}</DisplayString>
<StringView>m_pszData,su</StringView>
</Type>
Во время отладки можно выбрать значок лупы рядом с переменной, а затем выбрать Визуализатор текста для отображения строки, на которую указывает m_pszData.
Выражение {m_pszData,su}
содержит спецификатор формата C++ (su), чтобы значение отображалось как строка Юникода. Дополнительные сведения см. в статье Описатели формата в C++.
Элемент Expand
Необязательный узел Expand
настраивает дочерние элементы визуализированного типа при развертывании типа в окне переменной. Узел Expand
принимает список дочерних узлов, которые определяют дочерние элементы.
Если узел
Expand
не определен в записи визуализации, дочерние элементы используют правила расширений по умолчанию.Если узел
Expand
указан без дочерних узлов в нем, тип нельзя развернуть в окнах отладчика.
Расширение элемента
Элемент Item
— самый простой и самый распространенный элемент в узле Expand
. Item
определяет один дочерний элемент. Например, класс CRect
с полями top
, left
, right
и bottom
имеет следующую запись визуализации.
<Type Name="CRect">
<DisplayString>{{top={top} bottom={bottom} left={left} right={right}}}</DisplayString>
<Expand>
<Item Name="Width">right - left</Item>
<Item Name="Height">bottom - top</Item>
</Expand>
</Type>
В окне отладчика тип CRect
выглядит как в следующем примере.
Отладчик вычисляет выражения, указанные в элементах Width
и Height
, и выводит значения в столбце Значение окна переменной.
Отладчик автоматически создает узел [базовое представление] для каждого пользовательского расширения. На предыдущем снимке экрана отображается развернутый узел [базовое представление], чтобы показать отличие базового представления по умолчанию объекта от его визуализации Natvis. Расширение по умолчанию создает поддерево для базового класса и перечисляет все данные-члены базового класса в качестве дочерних элементов.
Примечание.
Если выражение элемента указывает на сложный тип, сам узел Item можно будет развернуть.
ArrayItems expansion
Используйте узел ArrayItems
, чтобы отладчик Visual Studio интерпретировал тип как массив и отображал его отдельные элементы. Хорошим примером является визуализация для std::vector
:
<Type Name="std::vector<*>">
<DisplayString>{{size = {_Mylast - _Myfirst}}}</DisplayString>
<Expand>
<Item Name="[size]">_Mylast - _Myfirst</Item>
<Item Name="[capacity]">(_Myend - _Myfirst)</Item>
<ArrayItems>
<Size>_Mylast - _Myfirst</Size>
<ValuePointer>_Myfirst</ValuePointer>
</ArrayItems>
</Expand>
</Type>
std::vector
указывает его отдельные элементы, когда он развернут в окне переменных:
Узел ArrayItems
должен иметь следующее.
- Выражение
Size
(которое должно вычисляться с получением целого числа) для отладчика, чтобы понять длину массива. - Выражение
ValuePointer
, которое указывает на первый элемент (который должен быть указателем типа элемента, отличного отvoid*
).
Значение по умолчанию нижней границы массива — 0. Чтобы переопределить это значение, используйте элемент LowerBound
. Примеры приведены в NATVIS-файлах, поставляемых с Visual Studio.
Примечание.
Оператор []
(например, vector[i]
) можно использовать с любой визуализацией одномерного массива, которая использует ArrayItems
, даже если сам тип (например, CATLArray
) не допускает использование этого оператора.
Можно также определить многомерные массивы. В этом случае отладчику требуется немного больше информации для правильного отображения дочерних элементов.
<Type Name="Concurrency::array<*,*>">
<DisplayString>extent = {_M_extent}</DisplayString>
<Expand>
<Item Name="extent">_M_extent</Item>
<ArrayItems Condition="_M_buffer_descriptor._M_data_ptr != 0">
<Direction>Forward</Direction>
<Rank>$T2</Rank>
<Size>_M_extent._M_base[$i]</Size>
<ValuePointer>($T1*) _M_buffer_descriptor._M_data_ptr</ValuePointer>
<LowerBound>0</LowerBound>
</ArrayItems>
</Expand>
</Type>
Direction
определяет, имеет ли массив порядок, основанный на строках или столбцах.Rank
указывает ранг массива.- Элемент
Size
принимает неявный параметр$i
, вместо которого подставляет индекс измерения для нахождения длины массива в этом измерении.- В предыдущем примере выражение
_M_extent.M_base[0]
должно дать длину 0-го измерения,_M_extent._M_base[1]
первую и т. д.
- В предыдущем примере выражение
LowerBound
указывает нижнюю границу каждого измерения массива. Для многомерных массивов можно указать выражение, использующее неявный параметр$i
. Параметр$i
будет заменен индексом измерения, чтобы найти нижнюю границу массива в этом измерении.- В предыдущем примере все измерения начинаются с 0. Однако если у вас есть
($i == 1) ? 1000 : 100
нижняя граница, то 0-е измерение начинается с 100, и первое измерение начинается с 1000.- , например,
[100, 1000], [100, 1001], [100, 1002], ... [101, 1000], [101, 1001],...
- , например,
- В предыдущем примере все измерения начинаются с 0. Однако если у вас есть
Здесь приводится пример того, как двумерный объект Concurrency::array
выглядит в окне отладчика.
Расширение IndexListItems
Расширение ArrayItems
можно использовать только в том случае, если элементы массива располагаются в памяти непрерывно. Отладчик переходит к следующему элементу, просто увеличивая свой указатель. Для управления индексом в узле значения используйте узлы IndexListItems
. Здесь приводится визуализация, использующая узел IndexListItems
.
<Type Name="Concurrency::multi_link_registry<*>">
<DisplayString>{{size = {_M_vector._M_index}}}</DisplayString>
<Expand>
<Item Name="[size]">_M_vector._M_index</Item>
<IndexListItems>
<Size>_M_vector._M_index</Size>
<ValueNode>*(_M_vector._M_array[$i])</ValueNode>
</IndexListItems>
</Expand>
</Type>
Единственное различие между ArrayItems
и IndexListItems
состоит в том, что ValueNode
ожидает полного выражения до i-го элемента с неявным параметром $i
.
Примечание.
Оператор []
(например, vector[i]
) можно использовать с любой визуализацией одномерного массива, которая использует IndexListItems
, даже если сам тип (например, CATLArray
) не допускает использование этого оператора.
Расширение LinkedListItems
Если визуализированный тип представляет связанный список, отладчик может отображать его дочерние элементы с помощью узла LinkedListItems
. Следующая визуализация для типа CAtlList
использует LinkedListItems
.
<Type Name="ATL::CAtlList<*,*>">
<DisplayString>{{Count = {m_nElements}}}</DisplayString>
<Expand>
<Item Name="Count">m_nElements</Item>
<LinkedListItems>
<Size>m_nElements</Size>
<HeadPointer>m_pHead</HeadPointer>
<NextPointer>m_pNext</NextPointer>
<ValueNode>m_element</ValueNode>
</LinkedListItems>
</Expand>
</Type>
Элемент Size
ссылается на длину списка. HeadPointer
указывает на первый элемент, NextPointer
ссылается на следующий элемент, а ValueNode
ссылается на значение элемента.
Отладчик вычисляет выражения NextPointer
и ValueNode
в контексте элемента узла LinkedListItems
, а не родительского типа списка. В предыдущем примере у CAtlList
есть класс CNode
(находящийся в atlcoll.h
), являющийся узлом связанного списка. m_pNext
и m_element
— поля этого класса CNode
, а не класса CAtlList
.
ValueNode
можно оставить пустым или использовать this
для ссылки на сам узел LinkedListItems
.
Расширение LinkedListItems
Расширение CustomListItems
позволяет записывать настраиваемую логику для обхода структуры данных, например хэш-таблицы. Используйте CustomListItems
для визуализации структур данных, в которых все, что нужно вычислить, можно выразить через выражения C++, но не вполне подходит под определение ArrayItems
, IndexListItems
или LinkedListItems
.
Exec
можно использовать для выполнения кода внутри расширения CustomListItems
с помощью переменных и объектов, определенных в расширении. С Exec
можно использовать логические операторы, арифметические операторы и операторы присваивания. Exec
нельзя применять для вычисления функций, за исключением встроенных функций отладчика, поддерживаемых вычислителем выражений C++.
Следующий визуализатор для CAtlMap
является отличным примером подходящих ситуаций для использования CustomListItems
.
<Type Name="ATL::CAtlMap<*,*,*,*>">
<AlternativeType Name="ATL::CMapToInterface<*,*,*>"/>
<AlternativeType Name="ATL::CMapToAutoPtr<*,*,*>"/>
<DisplayString>{{Count = {m_nElements}}}</DisplayString>
<Expand>
<CustomListItems MaxItemsPerView="5000" ExcludeView="Test">
<Variable Name="iBucket" InitialValue="-1" />
<Variable Name="pBucket" InitialValue="m_ppBins == nullptr ? nullptr : *m_ppBins" />
<Variable Name="iBucketIncrement" InitialValue="-1" />
<Size>m_nElements</Size>
<Exec>pBucket = nullptr</Exec>
<Loop>
<If Condition="pBucket == nullptr">
<Exec>iBucket++</Exec>
<Exec>iBucketIncrement = __findnonnull(m_ppBins + iBucket, m_nBins - iBucket)</Exec>
<Break Condition="iBucketIncrement == -1" />
<Exec>iBucket += iBucketIncrement</Exec>
<Exec>pBucket = m_ppBins[iBucket]</Exec>
</If>
<Item>pBucket,na</Item>
<Exec>pBucket = pBucket->m_pNext</Exec>
</Loop>
</CustomListItems>
</Expand>
</Type>
Расширение TreeItems
Если визуализированный тип представляет дерево, отладчик может пройти дерево отображать его дочерние элементы с помощью узла TreeItems
. Здесь приводится визуализация для типа std::map
с помощью узла TreeItems
.
<Type Name="std::map<*>">
<DisplayString>{{size = {_Mysize}}}</DisplayString>
<Expand>
<Item Name="[size]">_Mysize</Item>
<Item Name="[comp]">comp</Item>
<TreeItems>
<Size>_Mysize</Size>
<HeadPointer>_Myhead->_Parent</HeadPointer>
<LeftPointer>_Left</LeftPointer>
<RightPointer>_Right</RightPointer>
<ValueNode Condition="!((bool)_Isnil)">_Myval</ValueNode>
</TreeItems>
</Expand>
</Type>
Синтаксис очень похож на узел LinkedListItems
. LeftPointer
, RightPointer
и ValueNode
вычисляются в контексте класса узла дерева. ValueNode
можно оставить пустым или использовать this
для ссылки на сам узел TreeItems
.
Расширение ExpandedItem
Элемент ExpandedItem
создает агрегированное дочернее представление путем отображения свойств базовых классов или данных-членов так, как будто они являются дочерними элементами визуализируемого типа. Отладчик вычисляет указанное выражение и добавляет дочерние узлы результата в список дочерних элементов визуализируемого типа.
Например, тип интеллектуального указателя auto_ptr<vector<int>>
обычно отображается следующим образом.
Чтобы просмотреть значения вектора, необходимо развернуть два уровня в окне переменных, проходя через член _Myptr
. При добавлении элемента ExpandedItem
можно исключить переменную _Myptr
из иерархии и просмотреть непосредственно элементы вектора elements:
<Type Name="std::auto_ptr<*>">
<DisplayString>auto_ptr {*_Myptr}</DisplayString>
<Expand>
<ExpandedItem>_Myptr</ExpandedItem>
</Expand>
</Type>
В следующем примере показано, как агрегировать свойства из базового класса в производный класс. Предположим, класс CPanel
является производным от CFrameworkElement
. Вместо повторения свойств, полученных из базового класса CFrameworkElement
, визуализация узла ExpandedItem
добавляет эти свойства в дочерний список класса CPanel
.
<Type Name="CPanel">
<DisplayString>{{Name = {*(m_pstrName)}}}</DisplayString>
<Expand>
<Item Name="IsItemsHost">(bool)m_bItemsHost</Item>
<ExpandedItem>*(CFrameworkElement*)this,nd</ExpandedItem>
</Expand>
</Type>
Здесь необходим спецификатор формата nd, который отключает сопоставление визуализации для производного класса. В противном случае выражение *(CFrameworkElement*)this
приведет к тому, что визуализация CPanel
будет применена снова, поскольку правила сопоставления типов визуализации по умолчанию считают ее наиболее подходящей. Использование спецификатора формата nd дает отладчику указание использовать визуализацию базового класса или расширение по умолчанию базового класса, если базовый класс не имеет визуализации.
Расширение искусственных элементов
Там, где элемент ExpandedItem
предоставляет более плоское представление данных, устраняя иерархии, узел Synthetic
делает обратное. Он позволяет создать искусственный дочерний элемент, который не является результатом выражения. Искусственный элемент может иметь собственные дочерние элементы. В следующем примере визуализация для типа Concurrency::array
использует узел Synthetic
, чтобы показать диагностическое сообщение пользователю.
<Type Name="Concurrency::array<*,*>">
<DisplayString>extent = {_M_extent}</DisplayString>
<Expand>
<Item Name="extent" Condition="_M_buffer_descriptor._M_data_ptr == 0">_M_extent</Item>
<ArrayItems Condition="_M_buffer_descriptor._M_data_ptr != 0">
<Rank>$T2</Rank>
<Size>_M_extent._M_base[$i]</Size>
<ValuePointer>($T1*) _M_buffer_descriptor._M_data_ptr</ValuePointer>
</ArrayItems>
<Synthetic Name="Array" Condition="_M_buffer_descriptor._M_data_ptr == 0">
<DisplayString>Array members can be viewed only under the GPU debugger</DisplayString>
</Synthetic>
</Expand>
</Type>
Расширение instrinsic
Пользовательская встроенная функция, которую можно вызвать из выражения. Элемент <Intrinsic>
должен сопровождаться компонентом отладчика, реализующим функцию через интерфейс IDkmIntrinsicFunctionEvaluator140.
<Type Name="std::vector<*>">
<Intrinsic Name="size" Expression="(size_t)(_Mypair._Myval2._Mylast - _Mypair._Myval2._Myfirst)" />
<Intrinsic Name="capacity" Expression="(size_t)(_Mypair._Myval2._Myend - _Mypair._Myval2._Myfirst)" />
<DisplayString>{{ size={size()} }}</DisplayString>
<Expand>
<Item Name="[capacity]" ExcludeView="simple">capacity()</Item>
<Item Name="[allocator]" ExcludeView="simple">_Mypair</Item>
<ArrayItems>
<Size>size()</Size>
<ValuePointer>_Mypair._Myval2._Myfirst</ValuePointer>
</ArrayItems>
</Expand>
</Type>
Элемент HResult
Элемент HResult
позволяет настраивать сведения, которые отображаются для HRESULT в окнах отладчика. Элемент HRValue
должен содержать 32-разрядное значение HRESULT, который требуется настроить. Элемент HRDescription
содержит сведения для отображения в окне отладчика.
<HResult Name="MY_E_COLLECTION_NOELEMENTS">
<HRValue>0xABC0123</HRValue>
<HRDescription>No elements in the collection.</HRDescription>
</HResult>
Элемент UIVisualizer
Элемент UIVisualizer
регистрирует подключаемый модуль графического визуализатора в отладчике. Графический визуализатор создает диалоговое окно или другой интерфейс, который отображает переменную или объект в соответствии с его типом данных. Подключаемый модуль визуализатора должен быть создан как VSPackage и должен предоставлять службу, которая может использоваться отладчиком. NATVIS-файл содержит сведения о регистрации для подключаемого модуля, например его имя, глобальный уникальный идентификатор (GUID) предоставленной службы, а также типы, которые он может визуализировать.
Здесь приведен пример элемента UIVisualizer:
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<UIVisualizer ServiceId="{5452AFEA-3DF6-46BB-9177-C0B08F318025}"
Id="1" MenuName="Vector Visualizer"/>
<UIVisualizer ServiceId="{5452AFEA-3DF6-46BB-9177-C0B08F318025}"
Id="2" MenuName="List Visualizer"/>
.
.
</AutoVisualizer>
Пара атрибутов
ServiceId
-Id
определяетUIVisualizer
.ServiceId
является идентификатором GUID службы, предоставляемой пакетом визуализатора.Id
является уникальным идентификатором, отличающим визуализаторы, если служба предоставляет несколько визуализаторов. В предыдущем примере та же служба визуализатора предоставляет два визуализатора.Атрибут
MenuName
определяет имя визуализатора, отображаемое в раскрывающемся списке рядом с значок с увеличением стекла в отладчике. Например:
Каждый тип, определенный в NATVIS-файле, должен явно указать визуализаторы пользовательского интерфейса, которые могут отображать их. Отладчик сопоставляет ссылки на визуализаторы в записях типов для сопоставления типов с зарегистрированными визуализаторами. Например, следующая запись типа для std::vector
ссылается на UIVisualizer
в предыдущем примере.
<Type Name="std::vector<int,*>">
<UIVisualizer ServiceId="{5452AFEA-3DF6-46BB-9177-C0B08F318025}" Id="1" />
</Type>
Пример UIVisualizer
см. в расширении Image Watch, используемом для просмотра находящихся в памяти растровых изображений.
Элемент CustomVisualizer
CustomVisualizer
— это точка расширяемости, указывающая расширение VSIX, которое записывается для управления визуализациями в Visual Studio Code. Дополнительные сведения о создании расширений VSIX см. в статье Пакет SDK для Visual Studio.
Написание настраиваемого визуализатора гораздо сложнее, чем написание XML-определения Natvis, но с ним не связаны ограничения поддержки Natvis. Настраиваемые визуализаторы имеют доступ к полному набору API-интерфейсов расширяемости отладчика, которые можно использовать для запроса и изменения отлаживаемого процесса или взаимодействия с другими частями Visual Studio.
Для элементов CustomVisualizer
можно использовать атрибуты Condition
, IncludeView
и ExcludeView
.
Ограничения
Настройки Natvis работают с классами и структурами, но не с определениями типов.
Natvis не поддерживает визуализаторы для примитивных типов (например, int
, bool
) или для указателей на примитивные типы. В этом сценарии одним из вариантов является использование подходящего описателя формата. Например, если в коде используется double* mydoublearray
, в окне Контрольное значение отладчика можно использовать описатель формата массива, например выражение mydoublearray, [100]
, которое показывает первые 100 элементов.