Создание пользовательских представлений объектов C++ в отладчике с помощью платформы Natvis
Фреймворк Natvis Visual Studio
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++
В любой проект C++ можно добавить файл .natvis.
Добавление нового файла .natvis:
Выберите узел проекта C++ в обозревателе решенийи выберите Project>Добавить новый элементили щелкните проект правой кнопкой мыши и выберите Добавить>новый элемент.
Если вы не видите все шаблоны элементов, выберите Показать все шаблоны.
В диалоговом окне Добавление нового элемента выберите служебную программу Visual C>++>файла визуализации отладчика (.natvis).
Дайте имя файлу и выберите Добавить.
Новый файл добавляется в обозревателе решенийи открывается в области документов Visual Studio.
Отладчик Visual Studio автоматически загружает файлы .natvis в проектах на C++, а по умолчанию также включает их в файл .pdb при сборке проекта в Visual Studio. При отладке встроенного приложения отладчик загружает файл .natvis из файла PDB, даже если у вас нет открытого проекта. Если вы не хотите, чтобы файл .natvis включен в PDB, его можно исключить из встроенного PDB- файла.
Выберите файл .natvis в Проводнике решенийи нажмите на значок Свойства или щелкните файл правой кнопкой мыши и выберите Свойства.
В раскрывающемся списке нажмите на стрелку рядом с Исключено из сборки, выберите Даи нажмите ОК.
Заметка
Для отладки исполняемых проектов используйте элементы решения для добавления файлов .natvis, которые не находятся в .pdb, так как проект C++ отсутствует.
Заметка
Правила Natvis, загруженные из PDB применяются только к типам в модулях, на которые ссылается PDB. Например, если Module1.pdb содержит запись Natvis для типа с именем Test
, она применяется только к классу Test
в Module1.dll. Если другой модуль также определяет класс с именем Test
, к нему не применяется запись Module1.pdb Natvis.
Установка и регистрация файла .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 в интегрированной среде разработки. Откройте файл в том же экземпляре Visual Studio, с которым выполняется отладка, измените его и сохраните. Как только файл сохранен, окна Watch и Locals обновляются, чтобы отразить это изменение.
Вы также можете добавить или удалить файлы .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++) на Ошибка, Предупреждениеили Подробно, а затем нажмите кнопку ОК.
Ошибки отображаются в окне выходных данных
Справочник по синтаксису 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
может иметь типа, HResult, UIVisualizerи CustomVisualizer дочерних элементов.
Элемент типа
Базовый 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>
Вы можете ссылаться на параметры шаблона в записи визуализации с помощью макросов $T 1, $T 2 и т. д. Чтобы найти примеры этих макросов, ознакомьтесь с файлами .natvis, поставляемыми с Visual Studio.
Сопоставление типов визуализатора
Если запись визуализации не удается проверить, используется следующая доступная визуализация.
Наследуемый атрибут
Необязательный атрибут Inheritable
указывает, применяется ли визуализация только к базовому типу или к базовому типу и всем производным типам. Значение по умолчанию Inheritable
равно true
.
В следующем примере визуализация применяется только к типу BaseClass
:
<Type Name="Namespace::BaseClass" Inheritable="false">
<DisplayString>{{Count = {m_nSize}}}</DisplayString>
</Type>
Атрибут приоритета
Необязательный атрибут Priority
указывает порядок использования альтернативных определений, если определение не удается проанализировать. Возможные значения Priority
: Low
, MediumLow
,Medium
, MediumHigh
и High
. Значение по умолчанию — Medium
. Атрибут Priority
различает только приоритеты в том же файле .natvis.
В следующем примере сначала анализируется запись, соответствующая STL 2015. Если это не удалось проанализировать, он использует альтернативную запись для версии STL 2013:
<!-- 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
можно поместить на любой узел. Если подвыражение внутри необязательного узла не удалось проанализировать, отладчик игнорирует этот узел, но применяет остальные правила 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
доступен для многих элементов визуализации и указывает, когда следует использовать правило визуализации. Если значение выражения внутри атрибута условия равно 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
указан без дочерних узлов под ним, тип не может быть развернут в окнах отладчика.
Расширение элементов
Элемент 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 указывает на сложный тип, Item сам по себе является расширяемым узлом.
Расширение ArrayItems
Используйте узел 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
.
Расширение элементов пользовательского списка
Расширение 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
из иерархии и напрямую просмотреть векторные элементы:
<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>
Встроенное расширение
Пользовательская встроенная функция, которую можно вызвать из выражения. Элемент <Intrinsic>
должен сопровождаться компонентом отладчика, реализующим функцию через интерфейс IDkmIntrinsicFunctionEvaluator140. Дополнительные сведения о реализации пользовательской встроенной функции см. в реализации пользовательской встроенной функции NatVis.
<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
можно увидеть в расширении "Просмотр изображений", используемом для просмотра изображений в памяти.
Элемент CustomVisualizer
CustomVisualizer
— это точка расширяемости, указывающая расширение VSIX, записываемое для управления визуализациями в Visual Studio Code. Дополнительные сведения о написании расширений VSIX см. в пакете SDK Visual Studio.
Создание пользовательского визуализатора требует гораздо больше работы, чем определение в формате XML Natvis, но вы не ограничены рамками того, что Natvis поддерживает или не поддерживает. Пользовательские визуализаторы имеют доступ к полному набору API расширяемости отладчика, которые могут запрашивать и изменять процесс отладки или взаимодействовать с другими частями Visual Studio.
Атрибуты Condition
, IncludeView
и ExcludeView
можно использовать для элементов CustomVisualizer
.
Ограничения
Настройки Natvis работают с классами и структурами, но не с типами синонимов.
Natvis не поддерживает визуализаторы для примитивных типов (например, int
, bool
) или указателей на примитивные типы. В этом сценарии один из вариантов — использовать описатель формата подходящим для вашего варианта использования. Например, если вы используете double* mydoublearray
в своем коде, вы можете использовать спецификатор формата массива в окне наблюдения отладчика, например, выражение mydoublearray, [100]
, которое показывает первые 100 элементов.