Compartilhar via


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

A estrutura Natvis do Visual Studio personaliza a maneira como os tipos nativos aparecem nas janelas de variáveis do depurador, como as janelas Locais e Inspeção, e em Dicas de Dados. As visualizações de Natvis podem ajudar a tornar os tipos que você cria 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.

Observação

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

Visualizações de 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 de depurador sem visualizações personalizadas aplicadas.

Visualização padrão da caixa de texto

A linha realçada mostra a Text propriedade da TextBox classe. 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 personalizado Natvis são aplicadas. Os membros importantes da classe aparecem juntos e o depurador mostra o valor da cadeia de caracteres subjacente do tipo de cadeia de caracteres personalizada.

Dados de TextBox usando visualizador

Usar arquivos .natvis em projetos C++

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

A estrutura básica de um arquivo .natvis é um ou mais Type elementos que representam entradas de visualização. O nome totalmente qualificado de cada Type elemento é especificado em seu Name atributo.

<?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 <VS\Installation Folder>\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 Project>Adicionar novo item ou clique com o botão direito do mouse no projeto e selecione Adicionar>Novo item.

    Se você não vir 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 ao Gerenciador de Soluções e é aberto no painel de documentos do Visual Studio.

O depurador do Visual Studio carrega arquivos .natvis em projetos C++ automaticamente e, por padrão, também os inclui no arquivo .pdb quando o projeto é compilado. Se você depurar o aplicativo compilado, 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, exclua-o 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, selecione OK.

Observação

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á nenhum projeto C++ disponível.

Observação

As regras de 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 nomeado Test, ela só se aplicará à Test classe em Module1.dll. Se outro módulo também definir uma classe chamada Test, a entrada do Natvis Module1.pdb 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 eles estejam instalados, todos os arquivos .natvis registrados são automaticamente coletados 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 eles se apliquem a vários projetos.

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

  1. Todos 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 de sistema (<Pasta> de Instalação do Microsoft Visual Studio\Common7\Packages\Debugger\Visualizadores). 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 enquanto faz debug

Você pode modificar um arquivo .natvis no IDE durante a depuração de seu 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 Inspeção e Locais serã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 adiciona ou remove 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 . 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 no operador de contexto (C++), lembre-se do seguinte:

  • As expressões Natvis são avaliadas no contexto do objeto em visualização, não no frame de pilha 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 as funções intrínsecas do depurador são livres de efeito colateral, elas 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 nos especificadores de formato no C++. Os especificadores de formato são ignorados quando a entrada é usada internamente pelo Natvis, como uma expressão Size em uma expansão ArrayItems.

Observação

Como o documento Natvis é XML, suas expressões não podem usar diretamente o símbolo &, maior que, menor que ou os operadores de deslocamento. Você deve escapar desses caracteres no conteúdo do item e nas declaraçõ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\>

Visualizações Natvis

Você pode definir diferentes visualizações Natvis para exibir tipos de formas diferentes. Por exemplo, o snippet a seguir mostra uma visualização de std::vector que define uma visualização simplificada chamada simple. Os elementos DisplayString e ArrayItems são exibidos 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 definir uma exibição alternativa. A exibição simples aparece como vec,view(simple):

Janela de 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:

  1. Abra o painel Ferramentas>Opções e expanda a seção Todas as Configurações>Depuração>Geral. A ação Depurar>Opções abre o painel na mesma seção.

  2. Em Janela de Saída>Configurações Gerais de Saída, defina a opção de mensagens de diagnóstico Natvis (somente C++) como Erro, Aviso ou Detalhado.

  1. Abra a caixa de diálogo Ferramentas>Opções e expanda a seção Depuração>Geral. A ação Opções de Depuração abre a caixa de diálogo na mesma seção.

  2. Em Janela de Saída>Configurações Gerais de Saída, defina a opção de mensagens de diagnóstico Natvis (somente C++) como Erro, Aviso ou Detalhado.

  3. Selecione OK.

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

Referência de sintaxe do 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 de namespace xmlns:.

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

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

Elemento Type

Um básico Type se parece com este exemplo:

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

O Type elemento especifica:

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

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

  3. Como devem ser os membros do tipo quando o usuário expande o tipo na janela de variáveis (o nó Expand).

Classes com modelo

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

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 uma 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 enviados com o Visual Studio.

Compatibilidade de tipos 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 opcional Inheritable 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 BaseClass tipo:

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

Atributo de prioridade

O atributo opcional Priority especifica a ordem na qual usar definições alternativas, se uma definição não for analisada. Os valores possíveis de Priority são: Low, MediumLow, Medium, MediumHigh e High. O valor padrão é Medium. O Priority 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 Type regras. No tipo a seguir, [State] não é opcional, mas [Exception] é opcional. Se MyNamespace::MyClass tiver um campo chamado _M_exceptionHolder, tanto o nó [State] quanto o nó [Exception] serão exibidos, mas se não houver um 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 opcional Condition 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 false, a regra de visualização não se aplicará. Se for avaliado como true, ou não existir um atributo Condition, a visualização aplica-se. 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 DisplayString elementos para um tipo de ponteiro inteligente. Quando o _Myptr membro está vazio, a condição do primeiro DisplayString elemento é resolvida para true, de modo que o formulário seja exibido. Quando o _Myptr membro não está vazio, a condição é avaliada como false, e o segundo DisplayString elemento é 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 ocultados em exibições específicas. Por exemplo, na seguinte especificação do Natvis para std::vector, o modo de 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 membros individuais.

Elemento de Versão

O Version elemento define o escopo de uma entrada de visualização para um módulo e uma versão específicos. O Version elemento 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 DirectUI::Border tipo encontrado na Windows.UI.Xaml.dll da versão 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>

Você não precisa dos dois Min e Max. São atributos opcionais. Não há suporte para caracteres curinga.

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

Elemento DisplayString

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

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

Significa que as 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 dentro de chaves para que seus valores sejam avaliados. O exemplo também mostra como você pode escapar de uma chave usando chaves duplas ( {{ ou }} ).

Observação

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

Elemento StringView

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

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

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

Elemento CStringT DisplayString

Adicionar um StringView elemento 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 visualizador StringView

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

Expandir elemento

O nó opcional Expand personaliza os elementos filhos de um tipo visualizado ao expandir o tipo na janela de variáveis. O Expand nó aceita uma lista de nós filho que definem os elementos filho.

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

  • Se um Expand nó 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 mais básico e comum em um nó Expand. Item define um único elemento filho. Por exemplo, uma CRect classe com campostop, leftrighte 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 parece com este exemplo:

CRect com expansão de elemento Item

O depurador avalia as expressões especificadas nos elementos Width e Height, e mostra os valores na coluna Valor da janela de variáveis.

O depurador cria automaticamente o nó [Visualização Bruta] para cada expansão personalizada. A captura de tela anterior exibe o nó [Modo de Exibição Bruto] expandido, para mostrar como o modo de exibição bruto padrão do objeto difere de sua visualização 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.

Observação

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

Expansão do ArrayItems

Utilize 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 seus elementos individuais quando expandidos na janela variável:

std::vector usando a expansão de ArrayItems

O ArrayItems nó deve ter:

  • Uma Size expressão (que deve ser avaliada como um inteiro) para que o depurador entenda o comprimento da matriz.
  • Uma ValuePointer expressão 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 LowerBound elemento. Os arquivos .natvis enviados com o Visual Studio têm exemplos.

Observação

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

Veja a aparência de um objeto bidimensional Concurrency::array na janela do depurador:

Matriz bidimensional com expansão ArrayItems

Expansão de IndexListItems

Você pode usar ArrayItems expansão somente se os elementos da matriz forem dispostos contiguamente na memória. O depurador chega ao próximo elemento simplesmente incrementando seu ponteiro. Se você precisar manipular o índice para o nó de valor, use nós IndexListItems. Aqui está uma visualização com o 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 é a ValueNode, que espera a expressão completa para o elemento ith com o parâmetro implícito $i .

Observação

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 representar uma lista vinculada, o depurador poderá exibir seus elementos usando um nó LinkedListItems. A seguinte visualização para o CAtlList tipo 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 Size elemento 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 de nó LinkedListItems, e não no tipo de lista pai. No exemplo anterior, CAtlList possui uma classe CNode (encontrada em atlcoll.h) que é um nó da lista encadeada. m_pNext e m_element são campos dessa CNode classe, não da CAtlList classe.

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

Expansão CustomListItems

A CustomListItems expansão 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 CustomListItems expansão, 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 compatíveis com o 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 representar uma árvore, o depurador poderá percorrer a árvore e exibir seus filhos usando um TreeItems nó. Aqui está a visualização para o tipo std::map usando o 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ó de árvore. ValueNode pode ser deixado vazio ou usar this para se referir ao TreeItems nó em si.

Expansão do ExpandedItem

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

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

auto_ptr< vector< int>> expansão padrão

Para ver os valores do vetor, você precisa se aprofundar em dois níveis na janela da variável, passando pelo membro _Myptr. Ao adicionar um ExpandedItem elemento, você pode eliminar a _Myptr variável 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>

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

O exemplo a seguir mostra como agregar propriedades da classe base em uma classe derivada. Suponha que a CPanel classe deriva de CFrameworkElement. Em vez de repetir as propriedades provenientes da classe base CFrameworkElement, a visualização do nó ExpandedItem acrescenta 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 para a classe derivada é necessário aqui. Caso contrário, a expressão *(CFrameworkElement*)this fará com que a CPanel visualização seja 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 de classe base ou a expansão padrão se a classe base não tiver visualização.

Expansão de item sintético

Enquanto o elemento ExpandedItem oferece uma exibição mais simples dos dados eliminando hierarquias, o nó Synthetic faz o oposto. Ele 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 para o tipo Concurrency::array usa um nó Synthetic para exibir 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 <Intrinsic> elemento 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 função intrínseca personalizada do 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 HResult elemento permite personalizar as informações exibidas para um HRESULT nas janelas de depuração. O HRValue elemento deve conter o valor de 32 bits do HRESULT que deve ser personalizado. O HRDescription elemento 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 visualização gráfica com o 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 desenvolvido como um VSPackage e deve expor um serviço que o depurador possa 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 ServiceId - Id par de atributos identifica um UIVisualizer. O ServiceId 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 um nome de visualizador a ser exibido na lista suspensa ao lado do ícone de lupa no depurador. Por exemplo:

    atalho do menu UIVisualizerMenus de atalho do menu UIVisualizer

Cada tipo definido no arquivo .natvis deve listar explicitamente todos os visualizadores de interface do usuário que possam exibi-lo. O depurador alinha as referências do visualizador nos registros de tipo com os visualizadores registrados. Por exemplo, a seguinte entrada de tipo para std::vector referencia o UIVisualizer no exemplo precedente.

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

Você pode ver um exemplo de um UIVisualizer na extensão Image Watch, usada para visualizar 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 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, IncludeView e ExcludeView nos elementos CustomVisualizer.

Limitações

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

O Natvis não dá suporte a visualizadores para tipos primitivos (por exemplo, int, ) boolou 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 em seu código, poderá 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.