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


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

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

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

Замечание

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

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

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

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

Визуализация 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 файлы в папке установки Visual Studio<>\Common7\Packages\Debugger\Visualizers. Эти файлы имеют правила визуализации для многих распространенных типов и могут служить примерами для написания визуализаций для новых типов.

Добавление NATVIS-файла в проект C++

Вы можете добавить NATVIS-файл в любой проект C++.

Чтобы добавить новый NATVIS-файл , выполните приведенные ниже действия.

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

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

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

  3. Назовите файл и нажмите кнопку "Добавить".

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

Отладчик Visual Studio загружает файлы natvis в проектах C++ автоматически, а по умолчанию также включает их в PDB-файл при сборке проекта. При отладке встроенного приложения отладчик загружает 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 заново в окне Immediate . Изменения вступили в силу без перезапуска сеанса отладки.

Также используйте команду .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>

В окне "Просмотр " используйте описатель формата представления , чтобы указать альтернативное представление. Простое представление отображается как vec,view(simple):

Окно

Ошибки Natvis

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

Чтобы включить диагностику Natvis, выполните приведенные ниже действия.

  1. Откройте панель"Параметры>" и разверните раздел"Общие параметры>>всех параметров". Действие «Параметры отладки» открывает панель в том же разделе.

  2. В разделе "Окно вывода>" Общие параметры вывода установите для опции "Диагностические сообщения Natvis (только C++)" значение Error, Warning или Verbose.

  1. Откройте диалоговое окно"Параметры>" и разверните раздел"Общие> отладке". Действие Debug>Options открывает диалоговое окно на тот же раздел.

  2. В разделе "Окно вывода>" Общие параметры вывода установите для опции "Диагностические сообщения Natvis (только C++)" значение Error, Warning или Verbose.

  3. Нажмите ОК.

Ошибки отображаются в окне вывода .

Справочник по синтаксису 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 Name="[fully qualified type name]">
  <DisplayString Condition="[Boolean expression]">[Display value]</DisplayString>
  <Expand>
    ...
  </Expand>
</Type>

Элемент Type указывает:

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

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

  3. Как должны быть представлены элементы типа, когда пользователь раскрывает его в окне переменных (узел Expand).

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

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

В следующем примере используется та же визуализация, является ли объект объектом 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, ,MediumLowMedium, 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 помогает избежать конфликтов имен, уменьшает вероятность непреднамеренных несоответствий и позволяет различным визуализациям для разных версий типов.

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

В следующем примере визуализация применима только для типа 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

В выражении DisplayString, x и 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 не указан в записи визуализации, дочерние элементы используют правила по умолчанию для расширения.

  • Если узел Expand указан без дочерних узлов, тип не может быть развернут в окнах отладчика.

Расширение элементов

Элемент Item является самым простым и общим элементом Expand узла. Item определяет один дочерний элемент. Например, CRect класс с полями top, leftrightи 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 CRect с расширением элемента Item

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

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

Замечание

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

Расширение элементов массива

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>

Единственное различие между и заключается в , который ожидает полное выражение для элемента -го с неявным параметром .

Замечание

Вы можете использовать оператор [], например 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 позволяет создавать пользовательскую логику для обхода структуры данных, такой как хэш-файл. Используйте 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_ptrvectorint расширение по умолчаниюРасширение по умолчанию

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

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

auto_ptr> ExpandedItem expansionExpandedItem expansion

В следующем примере показано, как агрегировать свойства из базового класса в производном классе. Предположим, что 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 приведeт к повторному применению визуализации 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>

Concurrency::Array с расширением синтетического элемента

Встроенное расширение

Пользовательская встроенная функция, которую можно вызвать из выражения. Элемент <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 в расширении Image Watch, используемом для просмотра растровых изображений в памяти.

Элемент CustomVisualizer

CustomVisualizer — это точка расширяемости, указывающая расширение VSIX, которое записывается для управления визуализациями в Visual Studio Code. Дополнительные сведения о написании расширений VSIX см. в пакете SDK Для Visual Studio.

Написание пользовательского визуализатора требует гораздо больше усилий, чем создание XML-определения Natvis, но у вас нет ограничений в том, что поддерживает или не поддерживает Natvis. Пользовательские визуализаторы имеют доступ к полному набору API расширяемости отладчика, которые могут запрашивать и изменять процесс отладки или взаимодействовать с другими частями Visual Studio.

Вы можете использовать атрибуты Condition, IncludeView и ExcludeView с элементами CustomVisualizer.

Ограничения

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

Natvis не поддерживает визуализаторы для примитивных типов (например, int, bool) или указателей на примитивные типы. В этом сценарии один из вариантов — использовать описатель формата , соответствующий вашему варианту использования. Например, если вы используете double* mydoublearray в коде, можно задать описатель формата массива в окне Watch отладчика, например, выражение mydoublearray, [100], отображающее первые 100 элементов.