Compartilhar via


Criar exibições personalizadas de objetos C++ no depurador usando a estrutura Natvis

A estrutura do Natvis do Visual Studio personaliza a maneira como os tipos nativos são exibidos nas janelas variáveis do depurador, como as janelas Locais e Inspeção e em DataTips. As visualizações do Natvis podem ajudar a tornar os tipos criados mais visíveis durante a depuração.

O Natvis substitui o arquivo autoexp.dat em versões anteriores do Visual Studio por sintaxe XML, melhor diagnóstico, controle de versão e suporte a vários arquivos.

Nota

As personalizações de Natvis funcionam com classes e estruturas, mas não com typedefs.

Visualizações do Natvis

Use a estrutura natvis para criar regras de visualização para os tipos criados, para que os desenvolvedores possam vê-las com mais facilidade durante a depuração.

Por exemplo, a ilustração a seguir mostra uma variável do tipo Windows::UI::XAML::Controls::TextBox em uma janela do depurador sem visualizações personalizadas aplicadas.

Visualização padrão do TextBox

A linha realçada mostra a propriedade Text da classe TextBox. A hierarquia de classe complexa dificulta a localização dessa propriedade. O depurador não sabe como interpretar o tipo de cadeia de caracteres personalizada, portanto, você não pode ver a cadeia de caracteres mantida dentro da caixa de texto.

O mesmo TextBox parece muito mais simples na janela de variáveis quando as regras do Visualizador de Regras Personalizadas Natvis são aplicadas. Os membros importantes da classe são exibidos juntos e o depurador mostra o valor da cadeia de caracteres subjacente do tipo de cadeia de caracteres personalizada.

Dados do TextBox usando o visualizador

Usar arquivos .natvis em projetos C++

O Natvis usa arquivos .natvis para especificar as regras de visualização. Um arquivo .natvis é um arquivo XML com uma extensão .natvis. O esquema Natvis é definido em <pasta de instalação do VS>\Xml\Schemas\1033\natvis.xsd.

A estrutura básica de um arquivo .natvis é um ou mais elementos Type que representam entradas de visualização. O nome totalmente qualificado de cada elemento Type é especificado em seu atributo 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>

O Visual Studio fornece alguns arquivos .natvis na pasta <Pasta de Instalação do VS>\Common7\Packages\Debugger\Visualizers. Esses arquivos têm regras de visualização para muitos tipos comuns e podem servir como exemplos para gravar visualizações para novos tipos.

Adicionar um arquivo .natvis a um projeto C++

Você pode adicionar um arquivo .natvis a qualquer projeto C++.

Para adicionar um novo arquivo .natvis:

  1. Selecione o nó de projeto do C++ no Gerenciador de Soluções , e selecione Projeto>Adicionar Novo Item, ou clique com o botão direito do mouse no projeto e selecione Adicionar>Novo Item.

    Caso você não veja todos os modelos de item, escolha Mostrar todos os modelos.

  2. Na caixa de diálogo Adicionar Novo Item, selecione Visual C++>Utilitário>Arquivo de visualização do depurador (.natvis).

  3. Nomeie o arquivo e selecione Adicionar.

    O novo arquivo é adicionado a do Gerenciador de Soluções e é aberto no painel de documentos do Visual Studio.

O depurador do Visual Studio carregará os arquivos .natvis nos projetos C++ automaticamente e, por padrão, também os incluirá no arquivo .pdb, quando o projeto for compilado. Se você depurar o aplicativo criado, o depurador carregará o arquivo .natvis do arquivo .pdb, mesmo que você não tenha o projeto aberto. Se você não quiser que o arquivo .natvis seja incluído no .pdb, pode excluí-lo do arquivo .pdb criado.

Para excluir um arquivo .natvis de um .pdb:

  1. Selecione o arquivo .natvis no Gerenciador de Soluções e selecione o ícone Propriedades ou clique com o botão direito do mouse no arquivo e selecione Propriedades.

  2. Clique na seta ao lado de Excluído do Build na lista suspensa e selecione Sim. Em seguida, clique em OK.

Nota

Para depurar projetos executáveis, use os itens da solução para adicionar arquivos .natvis que não estejam no .pdb, pois não há projetos C++ disponíveis.

Nota

As regras do Natvis carregadas de um .pdb se aplicam somente aos tipos nos módulos aos quais o .pdb se refere. Por exemplo, se Module1.pdb tiver uma entrada Natvis para um tipo chamado Test, ela só se aplicará à classe Test em Module1.dll. Se outro módulo também definir uma classe chamada Test, a entrada Module1.pdb Natvis não se aplicará a ela.

Para instalar e registrar um arquivo .natvis por meio de um pacote VSIX:

Um pacote VSIX pode instalar e registrar arquivos .natvis. Não importa onde estejam instalados, todos os arquivos registrados .natvis são automaticamente detectados durante a depuração.

  1. Inclua o arquivo .natvis no pacote VSIX. Por exemplo, para o seguinte arquivo de projeto:

    <?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. Registre o arquivo .natvis no arquivo 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>
    

Localizações do arquivo Natvis

Você pode adicionar arquivos .natvis ao diretório do usuário ou a um diretório do sistema, se quiser que se apliquem a vários projetos.

Os arquivos de .natvis são avaliados na seguinte ordem:

  1. Os arquivos .natvis inseridos em um .pdb que você está depurando, a menos que exista um arquivo com o mesmo nome no projeto carregado.

  2. Todos os arquivos .natvis que estão em um projeto C++ carregado ou em uma solução de nível superior. Esse grupo inclui todos os projetos C++ carregados, incluindo bibliotecas de classes, mas não projetos em outros idiomas.

  3. Todos os arquivos .natvis instalados e registrados por meio de um pacote VSIX.

  1. O diretório Natvis específico do usuário (por exemplo, %USERPROFILE%\Documents\Visual Studio 2022\Visualizers).
  1. O diretório Natvis específico do usuário (por exemplo, %USERPROFILE%\Documents\Visual Studio 2019\Visualizers).
  1. O diretório Natvis em todo o sistema (<pasta de instalação do Microsoft Visual Studio>\Common7\Packages\Debugger\Visualizers). Esse diretório tem os arquivos .natvis instalados com o Visual Studio. Se você tiver permissões de administrador, poderá adicionar arquivos a esse diretório.

Modificar arquivos .natvis durante a depuração

Você pode modificar um arquivo .natvis no IDE durante a depuração do projeto. Abra o arquivo na mesma instância do Visual Studio com o qual você está depurando, modifique-o e salve-o. Assim que o arquivo for salvo, as janelas Watch e Locals são atualizadas para refletir a alteração.

Você também pode adicionar ou excluir arquivos .natvis em uma solução que você está depurando e o Visual Studio adicionará ou removerá as visualizações relevantes.

Não é possível atualizar arquivos .natvis inseridos em arquivos .pdb durante a depuração.

Se você modificar o arquivo .natvis fora do Visual Studio, as alterações não entrarão em vigor automaticamente. Para atualizar as janelas do depurador, você pode reavaliar o comando .natvisreload na janela Imediata. Em seguida, as alterações entrarão em vigor sem reiniciar a sessão de depuração.

Use também o comando .natvisreload para atualizar o arquivo .natvis para uma versão mais recente. Por exemplo, o arquivo .natvis pode ser verificado no controle do código-fonte e você deseja pegar as alterações recentes feitas por outra pessoa.

Expressões e formatação

As visualizações de natvis usam expressões C++ para especificar os itens de dados a serem exibidos. Além dos aprimoramentos e limitações das expressões C++ no depurador, que são descritos em operador de contexto (C++), esteja ciente do seguinte:

  • As expressões do Natvis são avaliadas no contexto do objeto que está sendo visualizado, não do registro de ativação atual. Por exemplo, x em uma expressão Natvis refere-se ao campo chamado x no objeto que está sendo visualizado, não a uma variável local chamada x na função atual. Você não pode acessar variáveis locais em expressões Natvis, embora possa acessar variáveis globais.

  • Expressões Natvis não permitem avaliação de funções ou efeitos colaterais. As chamadas de função e os operadores de atribuição são ignorados. Como funções intrínsecas do depurador são sem efeitos colaterais, podem ser chamadas livremente de qualquer expressão Natvis, mesmo que outras chamadas de função não sejam permitidas.

  • Para controlar como uma expressão é exibida, você pode usar qualquer um dos especificadores de formato descritos em especificadores de formato no C++. Os especificadores de formato são ignorados quando a entrada é usada internamente pelo Natvis, como a expressão Size em uma expansão ArrayItems.

Nota

Como o documento Natvis é XML, suas expressões não podem utilizar diretamente os operadores: e comercial, maior que, menor que ou shift. Você deve evitar esses caracteres no corpo do item e nas instruções de condição. Por exemplo:
\<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\>

Exibições do Natvis

Você pode definir diferentes exibições do Natvis para exibir os tipos de maneiras diferentes. Por exemplo, aqui está uma visualização de std::vector que define uma exibição simplificada chamada simple. Os elementos DisplayString e ArrayItems são mostrados no modo de exibição padrão e no modo de exibição simple, enquanto os itens [size] e [capacity] não são exibidos no modo de exibição 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>

Na janela Inspeção, use o especificador de formato ,view para especificar uma exibição alternativa. A exibição simples aparece como vec,view(simple):

janela de inspeção Janela Inspeção com exibição simples

Erros de Natvis

Quando o depurador encontra erros em uma entrada de visualização, ele os ignora. Ele exibe o tipo em sua forma bruta ou escolhe outra visualização adequada. Você pode usar o diagnóstico natvis para entender por que o depurador ignorou uma entrada de visualização e para ver a sintaxe subjacente e analisar erros.

Para ativar o diagnóstico do Natvis:

  • Em Ferramentas>Opções (ou Depurar>Opções) >Depuração>Janela de Saída, defina Mensagens de diagnóstico do Natvis (somente C++) como Erro, Aviso ou Detalhado e selecione OK.

Os erros serão exibidos na janela Saída.

Referência de sintaxe Natvis

Os seguintes elementos e atributos podem ser usados no arquivo Natvis.

Elemento AutoVisualizer

O elemento AutoVisualizer é o nó raiz do arquivo .natvis e contém o atributo xmlns: do namespace.

<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
.
.
</AutoVisualizer>

O elemento AutoVisualizer pode ter os filhos Type, HResult, UIVisualizer e CustomVisualizer.

Elemento Type

Um Type básico é semelhante a este exemplo:

<Type Name="[fully qualified type name]">
  <DisplayString Condition="[Boolean expression]">[Display value]</DisplayString>
  <Expand>
    ...
  </Expand>
</Type>

O elemento Type especifica:

  1. Para qual tipo a visualização deve ser usada (o atributo Name).

  2. Como deve ser o valor de um objeto desse tipo (o elemento DisplayString).

  3. Como devem ser os membros do tipo quando o usuário expande o tipo em uma janela variável (o nó Expand).

Classes com modelo

O atributo Name do elemento Type aceita o asterisco * como caractere curinga que pode ser usado para nomes de classe template.

No exemplo a seguir, a mesma visualização é usada se o objeto é um CAtlArray<int> ou um CAtlArray<float>. Se houver uma entrada de visualização específica para um CAtlArray<float>, ela terá precedência sobre a genérica.

<Type Name="ATL::CAtlArray&lt;*&gt;">
    <DisplayString>{{Count = {m_nSize}}}</DisplayString>
</Type>

Você pode referenciar parâmetros de modelo na entrada de visualização usando macros $T 1, $T 2 e assim por diante. Para encontrar exemplos dessas macros, consulte os arquivos .natvis fornecidos com o Visual Studio.

Correspondência de tipo de visualizador

Se uma entrada de visualização não for validada, a próxima visualização disponível será usada.

Atributo herdável

O atributo Inheritable opcional especifica se uma visualização se aplica apenas a um tipo base ou a um tipo base e a todos os tipos derivados. O valor padrão de Inheritable é true.

No exemplo a seguir, a visualização se aplica somente ao tipo BaseClass:

<Type Name="Namespace::BaseClass" Inheritable="false">
    <DisplayString>{{Count = {m_nSize}}}</DisplayString>
</Type>

Atributo de prioridade

O atributo Priority opcional especifica a ordem na qual usar definições alternativas, se uma definição falhar ao analisar. Os valores possíveis de Priority são: Low, MediumLow,Medium, MediumHighe High. O valor padrão é Medium. O atributo distingue apenas entre as prioridades no mesmo arquivo .natvis .

O exemplo a seguir analisa primeiro a entrada que corresponde à STL de 2015. Se isso não for analisado, ele usará a entrada alternativa para a versão 2013 do STL:

<!-- 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>

Atributo opcional

Você pode colocar um atributo Optional em qualquer nó. Se uma subexpressão dentro de um nó opcional não for analisada, o depurador ignorará esse nó, mas aplicará o restante das regras de Type. No tipo a seguir, [State] não é opcional, mas [Exception] é opcional. Se MyNamespace::MyClass tiver um campo chamado _M_exceptionHolder, o nó [State] e o nó [Exception] aparecerão, mas se não houver nenhum campo _M_exceptionHolder, apenas o nó [State] será exibido.

<Type Name="MyNamespace::MyClass">
    <Expand>
      <Item Name="[State]">_M_State</Item>
      <Item Name="[Exception]" Optional="true">_M_exceptionHolder</Item>
    </Expand>
</Type>

Atributo de condição

O atributo Condition opcional está disponível para muitos elementos de visualização e especifica quando usar uma regra de visualização. Se a expressão dentro do atributo de condição for resolvida para false, a regra de visualização não se aplicará. Se ele for avaliado como trueou não houver nenhum atributo Condition, a visualização será aplicada. Você pode usar esse atributo para a lógica if-else nas entradas de visualização.

Por exemplo, a visualização a seguir tem dois elementos DisplayString para um tipo de ponteiro inteligente. Quando o membro _Myptr estiver vazio, a condição do primeiro elemento DisplayString será resolvida para true, de modo que o formulário seja exibido. Quando o membro _Myptr não está vazio, a condição é avaliada como falsee o segundo elemento DisplayString é exibido.

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

Atributos IncludeView e ExcludeView

Os atributos IncludeView e ExcludeView especificam elementos a serem exibidos ou não em exibições específicas. Por exemplo, na seguinte especificação natvis de std::vector, a exibição simple não exibe os itens [size] e [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>

Você pode usar os atributos IncludeView e ExcludeView em tipos e em membros individuais.

Elemento Version

O elemento Version define como escopo uma entrada de visualização para um módulo e uma versão específicos. O elemento Version ajuda a evitar colisões de nome, reduz incompatibilidades inadvertidas e permite visualizações diferentes para versões de tipo diferentes.

Se um arquivo de cabeçalho comum usado por módulos diferentes definir um tipo, a visualização com versão será exibida somente quando o tipo estiver na versão do módulo especificada.

No exemplo a seguir, a visualização é aplicável somente para o tipo DirectUI::Border encontrado no Windows.UI.Xaml.dll da versão 1.0 para a 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>

Você não precisa de Min e Max. Eles são atributos opcionais. Não há suporte para caracteres curinga.

O atributo Name está no formato filename.ext, como hello.exe ou some.dll. Nenhum nome de caminho é permitido.

Elemento DisplayString

O elemento DisplayString especifica uma cadeia de caracteres a ser mostrada como o valor de uma variável. Aceita cadeias de caracteres arbitrárias misturadas a expressões. Tudo que está dentro das chaves é interpretado como uma expressão. Por exemplo, a seguinte entrada DisplayString:

<Type Name="CPoint">
  <DisplayString>{{x={x} y={y}}}</DisplayString>
</Type>

Significa que variáveis do tipo CPoint são exibidas como nesta ilustração:

Usar um elemento DisplayString

Na expressão DisplayString, x e y, que são membros de CPoint, estão entre chaves, portanto, os valores são avaliados. O exemplo também mostra como você pode evitar uma chave usando duas chaves ({{ ou }}).

Nota

O elemento DisplayString é o único elemento que aceita cadeias de caracteres arbitrárias e a sintaxe da chave. Todos os outros elementos de visualização aceitam apenas expressões que o depurador pode avaliar.

Elemento StringView

O elemento StringView define um valor que o depurador pode enviar para o visualizador de texto interno. Por exemplo, dada a seguinte visualização para o tipo ATL::CStringT:

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

O objeto CStringT é exibido em uma janela variável como este exemplo:

Elemento CStringT DisplayString

Adicionar um elemento StringView informa ao depurador que ele pode exibir o valor como uma visualização de texto.

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

Durante a depuração, você pode selecionar o ícone de lupa ao lado da variável e, em seguida, selecionar Visualizador de Texto para exibir a cadeia de caracteres para a qual m_pszData aponta.

Dados CStringT com o visualizador StringView

A expressão {m_pszData,su} inclui um especificador de formato C++ su, para exibir o valor como uma cadeia de caracteres Unicode. Para obter mais informações, confira Especificadores de formato no C++.

Expandir elemento

O nó opcional Expand personaliza os filhos de um tipo visualizado quando você expande o tipo em uma janela variável. O nó Expand aceita uma lista de nós filhos que definem os elementos filhos.

  • Se um nó Expand não for especificado em uma entrada de visualização, os filhos usarão as regras de expansão padrão.

  • Se um nó Expand for especificado sem nós filhos abaixo dele, o tipo não será expansível nas janelas do depurador.

Expansão de item

O elemento Item é o elemento mais básico e comum em um nó Expand. Item define um único elemento filho. Por exemplo, uma classe CRect com campos top, left, righte bottom tem a seguinte entrada de visualização:

<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>

Na janela do depurador, o tipo CRect aparece como neste exemplo:

CRect com expansão do elemento Item

O depurador avalia as expressões especificadas nos elementos Width e Height e mostra os valores na coluna Value da janela variável.

O depurador cria automaticamente o nó [Raw View] para cada expansão personalizada. A captura de tela anterior exibe o nó [Raw View] expandido para mostrar como a exibição bruta padrão do objeto é diferente da visualização do Natvis. A expansão padrão cria uma subárvore para a classe base e lista todos os membros de dados da classe base como filhos.

Nota

Se a expressão do elemento de item apontar para um tipo complexo, o nó Item será expansível.

Expansão do ArrayItems

Use o nó ArrayItems para que o depurador do Visual Studio interprete o tipo como uma matriz e exiba seus elementos individuais. A visualização para std::vector é um bom exemplo:

<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>

Um std::vector mostra os elementos individuais quando expandido na janela variável:

std::vector usando a expansão do ArrayItems

O nó ArrayItems precisa ter:

  • Uma expressão Size (que deve ser avaliada como um inteiro) para que o depurador entenda o comprimento da matriz.
  • Uma expressão ValuePointer que aponta para o primeiro elemento (que deve ser um ponteiro de um tipo de elemento que não seja void*).

O valor padrão do limite inferior da matriz é 0. Para substituir o valor, use um elemento LowerBound. Os arquivos .natvis fornecidos com o Visual Studio têm exemplos.

Nota

Você pode usar o operador [], por exemplo, vector[i], com qualquer visualização de matriz unidimensional que usa ArrayItems, mesmo que o próprio tipo (por exemplo, CATLArray) não permita esse operador.

Você também pode especificar matrizes multidimensionais. Nesse caso, o depurador precisa de um pouco mais de informações para exibir corretamente os elementos filho:

<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 especifica se a matriz está na ordem row-major ou column-major.
  • Rank especifica a classificação da matriz.
  • O elemento Size aceita o parâmetro de $i implícito, que ele substitui pelo índice de dimensão para localizar o comprimento da matriz nessa dimensão.
    • No exemplo anterior, a expressão _M_extent.M_base[0] deve dar o comprimento da 0ª dimensão, _M_extent._M_base[1] a primeira e assim por diante.
  • O LowerBound especifica o limite inferior de cada dimensão da matriz. Para matrizes multidimensionais, você pode especificar uma expressão que usa o parâmetro de $i implícito. O parâmetro $i será substituído pelo índice de dimensão para localizar o limite inferior da matriz nessa dimensão.
    • No exemplo anterior, todas as dimensões serão iniciadas em 0. No entanto, se você tiver ($i == 1) ? 1000 : 100 como o limite inferior, a 0ª dimensão começará em 100 e a primeira dimensão começará em 1000.
      • , como [100, 1000], [100, 1001], [100, 1002], ... [101, 1000], [101, 1001],...

Veja como um objeto Concurrency::array bidimensional fica na janela do depurador:

Matriz bidimensional com expansão de ArrayItems

Expansão de IndexListItems

Você poderá usar a expansão ArrayItems apenas se os elementos da matriz forem dispostos contiguamente na memória. O depurador chega ao elemento seguinte incrementando o ponteiro. Se você precisar manipular o índice para o nó de valor, use os nós IndexListItems. Esta é uma visualização com um nó 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>

A única diferença entre ArrayItems e IndexListItems é o ValueNode, que espera a expressão completa para o iº elemento com o parâmetro implícito $i.

Nota

Você pode usar o operador [], por exemplo, vector[i], com qualquer visualização de matriz unidimensional que usa IndexListItems, mesmo que o próprio tipo (por exemplo, CATLArray) não permita esse operador.

Expansão do LinkedListItems

Se o tipo visualizado representa uma lista vinculada, o depurador pode exibir seus filhos usando um nó LinkedListItems. A seguinte visualização para o tipo CAtlList usa 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>

O elemento Size refere-se ao comprimento da lista. HeadPointer aponta para o primeiro elemento, NextPointer refere-se ao próximo elemento e ValueNode refere-se ao valor do item.

O depurador avalia as expressões NextPointer e ValueNode no contexto do elemento node LinkedListItems, e não no tipo de lista pai. No exemplo anterior, CAtlList tem uma classe CNode (encontrada em atlcoll.h) que é um nó da lista vinculada. m_pNext e m_element são campos dessa classe CNode, não da classe CAtlList.

ValueNode pode ser deixado vazio ou usar this para se referir ao próprio nó LinkedListItems.

Expansão CustomListItems

A expansão CustomListItems permite que você escreva uma lógica personalizada para percorrer uma estrutura de dados, como um hashtable. Use CustomListItems para visualizar estruturas de dados que podem usar expressões C++ para tudo o que você precisa avaliar, mas não se ajustam ao molde para ArrayItems, IndexListItemsou LinkedListItems.

Você pode usar Exec para executar o código dentro de uma expansão CustomListItems, usando as variáveis e objetos definidos na expansão. Você pode usar operadores lógicos, operadores aritméticos e operadores de atribuição com Exec. Você não pode usar Exec para avaliar funções, exceto para funções intrínsecas do depurador suportadas pelo avaliador de expressão C++.

O visualizador a seguir para CAtlMap é um excelente exemplo em que CustomListItems é apropriado.

<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>

Expansão do TreeItems

Se o tipo visualizado representa uma árvore, o depurador pode percorrer a árvore e exibir seus filhos usando um nó TreeItems. Veja a visualização do tipo std::map usando um nó 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>

A sintaxe é semelhante ao nó LinkedListItems. LeftPointer, RightPointere ValueNode são avaliados no contexto da classe de nós de árvore. ValueNode pode ser deixado vazio ou usar this para se referir ao próprio nó TreeItems.

Expansão do ExpandedItem

O elemento ExpandedItem gera uma exibição filho agregada exibindo propriedades de classes base ou de membros de dados como se fossem filhos do tipo visualizado. O depurador avalia a expressão especificada e acrescenta os nós filhos do resultado à lista filho do tipo visualizado.

Por exemplo, o tipo de ponteiro inteligente auto_ptr<vector<int>> normalmente é exibido como:

Expansão padrão de auto_ptr<vector<int>>

Para ver os valores do vetor, você precisa aprofundar dois níveis na janela da variável, passando pelo membro _Myptr. Ao adicionar um elemento ExpandedItem, você pode eliminar a variável _Myptr da hierarquia e exibir diretamente os elementos de vetor:

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

Expansão de ExpandedItem auto_ptr<vector<int>>

O exemplo a seguir mostra como agregar propriedades da classe base em uma classe derivada. Suponha que a classe CPanel deriva de CFrameworkElement. Em vez de repetir as propriedades provenientes da classe base CFrameworkElement, a visualização do nó ExpandedItem adiciona essas propriedades à lista de filhos da classe CPanel.

<Type Name="CPanel">
  <DisplayString>{{Name = {*(m_pstrName)}}}</DisplayString>
  <Expand>
    <Item Name="IsItemsHost">(bool)m_bItemsHost</Item>
    <ExpandedItem>*(CFrameworkElement*)this,nd</ExpandedItem>
  </Expand>
</Type>

O especificador de formato nd, que desativa a correspondência de visualização da classe derivada é necessário aqui. Caso contrário, a expressão *(CFrameworkElement*)this faria com que a visualização CPanel fosse aplicada novamente, pois as regras de correspondência de tipo de visualização padrão a consideram a mais apropriada. Use o especificador de formato nd para instruir o depurador a usar a visualização da classe base ou a expansão padrão se a classe base não tiver visualização.

Expansão de item sintético

Embora o elemento ExpandedItem forneça uma visão mais simples dos dados eliminando hierarquias, o nó Synthetic faz o oposto. Isso permite que você crie um elemento filho artificial que não seja resultado de uma expressão. O elemento artificial pode ter elementos filho próprios. No exemplo a seguir, a visualização do tipo Concurrency::array usa um nó Synthetic para mostrar uma mensagem de diagnóstico ao usuário:

<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 com expansão de elemento sintético

Expansão intrínseca

Uma função intrínseca personalizada que pode ser chamada de uma expressão. Um elemento <Intrinsic> deve ser acompanhado por um componente de depurador que implementa a função por meio da interface IDkmIntrinsicFunctionEvaluator140. Para obter mais informações sobre como implementar uma função intrínseca personalizada, consulte Implementar a função intrínseca personalizada 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>

Elemento HResult

O elemento HResult permite que você personalize as informações exibidas para um HRESULT nas janelas do depurador. O elemento HRValue deve conter o valor de 32 bits do HRESULT que deve ser personalizado. O elemento HRDescription contém as informações a serem mostradas na janela do depurador.


<HResult Name="MY_E_COLLECTION_NOELEMENTS">
  <HRValue>0xABC0123</HRValue>
  <HRDescription>No elements in the collection.</HRDescription>
</HResult>

Elemento UIVisualizer

Um elemento UIVisualizer registra um plug-in de visualizador gráfico no depurador. Um visualizador gráfico cria uma caixa de diálogo ou outra interface que mostra uma variável ou objeto de forma consistente com seu tipo de dados. O plug-in do visualizador deve ser criado como um VSPackage e deve expor um serviço que o depurador pode consumir. O arquivo .natvis contém informações de registro para o plug-in, como seu nome, o GUID (identificador global exclusivo) do serviço exposto e os tipos que ele pode visualizar.

Aqui está um exemplo de um elemento 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>
  • Um par de atributos ServiceId - Id identifica um UIVisualizer. O ServiceId é o GUID do serviço que o pacote do visualizador expõe. Id é um identificador exclusivo que diferencia visualizadores, se um serviço fornecer mais de um. No exemplo anterior, o mesmo serviço de visualizador fornece dois visualizadores.

  • O atributo MenuName define o nome do visualizador a ser exibido na lista suspensa ao lado do ícone de lupa no depurador. Por exemplo:

    Menu de atalho do menu UIVisualizer

Cada tipo definido no arquivo .natvis deve listar explicitamente todos os visualizadores de IU que possam exibi-lo. O depurador corresponde as referências do visualizador nas entradas de tipo com os visualizadores registrados. Por exemplo, a seguinte entrada de tipo para std::vector faz referência à UIVisualizer no exemplo anterior.

<Type Name="std::vector&lt;int,*&gt;">
  <UIVisualizer ServiceId="{5452AFEA-3DF6-46BB-9177-C0B08F318025}" Id="1" />
</Type>

Você pode ver um exemplo de uma na extensão do Image Watch usada para exibir bitmaps na memória.

Elemento CustomVisualizer

CustomVisualizer é um ponto de extensibilidade que especifica uma extensão VSIX que você grava para controlar visualizações no Visual Studio Code. Para obter mais informações sobre como escrever extensões VSIX, consulte o do SDK do Visual Studio.

É muito mais trabalho escrever um visualizador personalizado do que uma definição de Natvis XML, mas você está livre de restrições sobre o que o Natvis faz ou não dá suporte. Visualizadores personalizados têm acesso ao conjunto completo de APIs de extensibilidade do depurador, que podem consultar e modificar o processo de depuração ou se comunicar com outras partes do Visual Studio.

Você pode usar os atributos Condition, IncludeViewe ExcludeView em elementos CustomVisualizer.

Limitações

As personalizações do Natvis são compatíveis com classes e structs, mas não com typedefs.

O Natvis não dá suporte a visualizadores para tipos primitivos (por exemplo, int, bool) ou para ponteiros para tipos primitivos. Nesse cenário, uma opção é usar o especificador de formato apropriado para seu caso de uso. Por exemplo, se você usar double* mydoublearray no seu código, pode usar um especificador de formato de matriz na janela Inspeção do depurador, como a expressão mydoublearray, [100], que mostra os primeiros 100 elementos.