Поделиться через


Создание пользовательских представлений объектов C++ в отладчике с помощью платформы Natvis

Фреймворк Natvis Visual Studio настраивает способ отображения собственных типов в окнах переменных отладчика, таких как Локальные и Контрольные окна, а также в DataTips. Визуализации Natvis могут помочь сделать типы, которые вы создаете более видимыми во время отладки.

Natvis заменяет файл autoexp.dat в более ранних версиях Visual Studio синтаксисом XML, более эффективной диагностикой, версией и поддержкой нескольких файлов.

Заметка

Настройки Natvis работают с классами и структурами, но не с типами синонимов.

Визуализации Natvis

Платформа Natvis используется для создания правил визуализации для создаваемых типов, чтобы разработчики могли легко видеть их во время отладки.

Например, на следующем рисунке показана переменная типа Windows::UI::XAML::Controls::TextBox в окне отладчика без применения пользовательских визуализаций.

стандартное отображение TextBox

Выделенная строка показывает свойство Text класса TextBox. Сложная иерархия классов затрудняет поиск этого свойства. Отладчик не знает, как интерпретировать пользовательский тип строки, поэтому вы не можете увидеть строку, находящуюся в текстовом поле.

Тот же TextBox выглядит гораздо проще в окне переменных при применении правил визуализатора Natvis. Важные члены класса отображаются вместе, а отладчик показывает базовое строковое значение пользовательского типа строки.

данные TextBox с помощью визуализатора

Использование файлов 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:

  1. Выберите узел проекта C++ в обозревателе решенийи выберите Project>Добавить новый элементили щелкните проект правой кнопкой мыши и выберите Добавить>новый элемент.

    Если вы не видите все шаблоны элементов, выберите Показать все шаблоны.

  2. В диалоговом окне Добавление нового элемента выберите служебную программу Visual C>++>файла визуализации отладчика (.natvis).

  3. Дайте имя файлу и выберите Добавить.

    Новый файл добавляется в обозревателе решенийи открывается в области документов Visual Studio.

Отладчик Visual Studio автоматически загружает файлы .natvis в проектах на C++, а по умолчанию также включает их в файл .pdb при сборке проекта в Visual Studio. При отладке встроенного приложения отладчик загружает файл .natvis из файла PDB, даже если у вас нет открытого проекта. Если вы не хотите, чтобы файл .natvis включен в PDB, его можно исключить из встроенного PDB- файла.

Исключить файл .natvis из .pdb:

  1. Выберите файл .natvis в Проводнике решенийи нажмите на значок Свойства или щелкните файл правой кнопкой мыши и выберите Свойства.

  2. В раскрывающемся списке нажмите на стрелку рядом с Исключено из сборки, выберите Даи нажмите ОК.

Заметка

Для отладки исполняемых проектов используйте элементы решения для добавления файлов .natvis, которые не находятся в .pdb, так как проект C++ отсутствует.

Заметка

Правила Natvis, загруженные из PDB применяются только к типам в модулях, на которые ссылается PDB. Например, если Module1.pdb содержит запись Natvis для типа с именем Test, она применяется только к классу Test в Module1.dll. Если другой модуль также определяет класс с именем Test, к нему не применяется запись Module1.pdb Natvis.

Установка и регистрация файла .natvis с помощью пакета VSIX:

Пакет VSIX может устанавливать и регистрировать файлы .natvis. Независимо от места установки, все зарегистрированные .natvis файлы автоматически учитываются во время отладки.

  1. Включите файл .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>
    
  2. Зарегистрируйте файл .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 оцениваются в следующем порядке:

  1. Все файлы .natvis, встроенные в отлаживаемый вами .pdb, если в загруженном проекте не существует файл с тем же именем.

  2. Все файлы .natvis, которые находятся в загруженном проекте C++ или решении верхнего уровня. Эта группа включает все загруженные проекты C++, включая библиотеки классов, но не проекты на других языках.

  3. Все файлы .natvis, установленные и зарегистрированные с помощью пакета VSIX.

  1. Каталог Natvis для конкретного пользователя (например, %USERPROFILE%\Documents\Visual Studio 2022\Visualizers).
  1. Каталог Natvis для конкретного пользователя (например, %USERPROFILE%\Documents\Visual Studio 2019\Visualizers).
  1. Каталог 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 \&gt;\&gt; 24),x\</Item\>
\<Item Name="HiByteStatus" Condition="(_flags \&amp; 0xFF000000) == 0"\>"None"\</Item\>
\<Item Name="HiByteStatus" Condition="(_flags \&amp; 0xFF000000) != 0"\>"Some"\</Item\>

Представления Natvis

Вы можете определить различные представления Natvis для отображения типов разными способами. Например, здесь представлена визуализация std::vector, которая определяет упрощенное представление с именем simple. Элементы DisplayString и ArrayItems отображаются в представлении по умолчанию и представлении simple, а элементы [size] и [capacity] не отображаются в представлении simple.

<Type Name="std::vector&lt;*&gt;">
    <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 указывает:

  1. Какой тип визуализации следует использовать (атрибут Name).

  2. Как должно выглядеть значение объекта этого типа (элемент DisplayString).

  3. Как должны выглядеть члены типа, когда пользователь расширяет тип в окне переменной (узел Expand).

Шаблонные классы

Атрибут Name элемента Type принимает звездочку * в качестве подстановочного знака, который можно использовать для имен шаблонных классов.

В следующем примере используется та же визуализация, является ли объект CAtlArray<int> или CAtlArray<float>. Если для CAtlArray<float>есть определенная запись визуализации, она имеет приоритет над универсальной.

<Type Name="ATL::CAtlArray&lt;*&gt;">
    <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&lt;*&gt;" Priority="MediumLow">
     <DisplayString>{_Callee}</DisplayString>
    <Expand>
        <ExpandedItem>_Callee</ExpandedItem>
    </Expand>
</Type>

<!-- VC 2015 -->
<Type Name="std::reference_wrapper&lt;*&gt;">
    <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&lt;*&gt;">
  <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&lt;*&gt;">
    <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

В выражении DisplayStringx и y, которые являются членами CPoint, находятся внутри фигурных скобков, поэтому их значения вычисляются. В примере также показано, как можно избежать фигурной скобки с помощью двойных фигурных скобок ({{ или }}).

Заметка

Элемент DisplayString является единственным элементом, принимаюющим произвольные строки и синтаксис фигурной скобки. Все остальные элементы визуализации принимают только выражения, которые может оценить отладчик.

Элемент StringView

Элемент StringView определяет значение, которое отладчик может отправить встроенному визуализатору текста. Например, учитывая следующую визуализацию для типа ATL::CStringT:

<Type Name="ATL::CStringT&lt;wchar_t,*&gt;">
  <DisplayString>{m_pszData,su}</DisplayString>
</Type>

Объект CStringT отображается в окне переменной, как в следующем примере:

элемент CStringT DisplayString

Добавление элемента StringView сообщает отладчику, что оно может отображать значение в виде визуализации текста.

<Type Name="ATL::CStringT&lt;wchar_t,*&gt;">
  <DisplayString>{m_pszData,su}</DisplayString>
  <StringView>m_pszData,su</StringView>
</Type>

Во время отладки вы можете выбрать значок лупы рядом с переменной, а затем выбрать текстовый визуализатор, чтобы отобразить строку, на которую m_pszData указывает.

данные CStringT с визуализатором StringView

Выражение {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 выглядит следующим образом:

CRect с расширением элемента 'Item'

Отладчик вычисляет выражения, указанные в элементах Width и Height, и отображает значения в столбце Значение окна переменной.

Отладчик автоматически создает узел [необработанное представление] для каждого пользовательского расширения. На предыдущем снимке экрана показан развернутый узел [Сырой вид], чтобы продемонстрировать, как сырой вид объекта по умолчанию отличается от его визуализации через Natvis. Расширение по умолчанию создает поддерев для базового класса и перечисляет все элементы данных базового класса в качестве дочерних элементов.

Заметка

Если выражение элемента item указывает на сложный тип, Item сам по себе является расширяемым узлом.

Расширение ArrayItems

Используйте узел ArrayItems, чтобы отладчик Visual Studio интерпретировал тип как массив и отображал отдельные элементы. Визуализация для std::vector является хорошим примером:

<Type Name="std::vector&lt;*&gt;">
  <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 отображаются по отдельности при развертывании в окне переменной.

std::vector с помощью расширения ArrayItems

Узел ArrayItems должен иметь следующее:

  • Выражение Size (которое должно оцениваться целым числом) для отладчика, чтобы понять длину массива.
  • Выражение ValuePointer, указывающее на первый элемент (который должен быть указателем типа элемента, который не void*).

Значение по умолчанию нижней границы массива равно 0. Чтобы переопределить значение, используйте элемент LowerBound. Файлы .natvis, которые поставляются с Visual Studio, содержат примеры.

Заметка

Оператор [] можно использовать, например vector[i], с любой визуализацией одномерного массива, использующего ArrayItems, даже если сам тип (например, CATLArray) не разрешает этот оператор.

Можно также указать многомерные массивы. В этом случае отладчику требуется немного больше информации для правильного отображения дочерних элементов:

<Type Name="Concurrency::array&lt;*,*&gt;">
  <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],...

Вот как выглядит двухмерный объект Concurrency::array в окне отладчика:

двухмерный массив с расширением ArrayItems

Расширение IndexListItems

Расширение ArrayItems можно использовать только в том случае, если элементы массива размещаются в памяти одновременно. Отладчик переходит к следующему элементу, просто увеличивая свой указатель. Если нужно изменить индекс в узле значения, используйте узлы IndexListItems. Ниже приведена визуализация с узлом IndexListItems:

<Type Name="Concurrency::multi_link_registry&lt;*&gt;">
  <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&lt;*,*&gt;">
  <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&lt;*,*,*,*&gt;">
    <AlternativeType Name="ATL::CMapToInterface&lt;*,*,*&gt;"/>
    <AlternativeType Name="ATL::CMapToAutoPtr&lt;*,*,*&gt;"/>
    <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&lt;*&gt;">
  <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>> обычно отображается следующим образом:

auto_ptr<вектор<int>> расширение по умолчанию

Чтобы увидеть значения вектора, необходимо углубиться на два уровня в окне переменной, пройти через элемент _Myptr. Добавив элемент ExpandedItem, можно исключить переменную _Myptr из иерархии и напрямую просмотреть векторные элементы:

<Type Name="std::auto_ptr&lt;*&gt;">
  <DisplayString>auto_ptr {*_Myptr}</DisplayString>
  <Expand>
    <ExpandedItem>_Myptr</ExpandedItem>
  </Expand>
</Type>

auto_ptr<векторе<int>> expandedItem

В следующем примере показано, как агрегировать свойства из базового класса в производном классе. Предположим, что класс 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&lt;*,*&gt;">
  <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&lt;*&gt;">
  <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 определяет имя визуализатора, отображаемое в раскрывающемся списке рядом с значком с увеличением стекла в отладчике. Например:

    меню быстрого доступа UIVisualizer меню быстрого доступа

Каждый тип, определенный в файле .natvis, должен явно перечислить все визуализаторы пользовательского интерфейса, которые могут отображать его. Отладчик сопоставляет ссылки визуализатора в типовых записях с зарегистрированными визуализаторами. Например, следующая запись типа для std::vector ссылается на UIVisualizer в предыдущем примере.

<Type Name="std::vector&lt;int,*&gt;">
  <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 элементов.