Criar exibições personalizadas dos objetos C++ no depurador usando a estrutura do 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 nas 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 do Natvis funcionam com classes e structs, mas não com typedefs.

Visualizações do Natvis

Você usará a estrutura do Natvis para criar regras de visualização para os tipos criados, para que os desenvolvedores possam vê-los 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, não é possível ver a cadeia de caracteres mantida no TextBox.

O mesmo TextBox parece muito mais simples na janela variável quando as regras do visualizador personalizado do 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 nos 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 do Natvis é definido em <VS Installation Folder>\Xml\Schemas\1033\natvis.xsd.

A estrutura básica de um arquivo .natvis é um ou mais elementos Type que representam as entradas de visualização. O nome totalmente qualificado de cada elemento Type é especificado no 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 de exemplo para gravar visualizações de 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ó do projeto 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 será adicionado ao 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 o ícone Propriedades ou clique com o botão direito do mouse no arquivo e selecione Propriedades.

  2. Na lista suspensa, selecione a seta ao lado de Excluído do Build; escolha Sim e 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á projetos C++ disponíveis.

Observação

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 o Module1.pdb tiver uma entrada do Natvis para um tipo chamado Test, ela só se aplicará à classe Test no Module1.dll. Se outro módulo também definir uma classe chamada Test, a entrada do Natvis do 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 em que local 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 arquivo de projeto a seguir:

    <?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 do 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 .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 os projetos em outras linguagens.

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

  1. O diretório do Natvis específico do usuário (por exemplo, %USERPROFILE%\Documents\Visual Studio 2022\Visualizers).
  1. O diretório do Natvis específico do usuário (por exemplo, %USERPROFILE%\Documents\Visual Studio 2019\Visualizers).
  1. O diretório do 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, pode 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 a qual você está depurando, modifique e salve-o. Assim que o arquivo é salvo, as janelas Inspeção e Locais 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. 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 convém pegar as alterações recentes feitas por outra pessoa.

Expressões e formatação

As visualizações do Natvis usam expressões do C++ para especificar os itens de dados a serem exibidos. Além dos aprimoramentos e das limitações das expressões C++ no depurador, que são descritas no 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 do Natvis se refere ao campo denominado x no objeto visualizado, não a uma variável local denominada x na função atual. Você não pode acessar as variáveis locais nas expressões do Natvis, embora possa acessar as variáveis globais.

  • As expressões do Natvis não permitem avaliação de função 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 não têm efeitos colaterais, elas podem ser livremente chamadas de qualquer expressão do Natvis, mesmo que outras chamadas de função estejam desabilitadas.

  • 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 do ArrayItems.

Observação

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, esta é uma visualização do std::vector que define uma exibição simplificada chamada simple. Os elementos DisplayString e ArrayItems aparecem na exibição padrão e no modo de exibição simple, enquanto os itens [size] e [capacity] não aparecem 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 Inspeção com exibição simples

Erros do Natvis

Quando o depurador encontra erros em uma entrada de visualização, ele os ignora. Ele exibe o tipo na forma bruta ou escolhe outra visualização adequada. Você pode usar o diagnóstico do Natvis para entender por que o depurador ignorou uma entrada de visualização e para ver a sintaxe subjacente e analisar os 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 do Natvis

Os seguintes elementos e atributos podem ser utilizados 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. Qual deve ser a aparência de um objeto desse tipo (semelhante à do 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 um asterisco * como caractere curinga que pode ser usado para nomes de classes com modelo.

No exemplo a seguir, a mesma visualização é usada caso o objeto seja 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 os parâmetros do modelo na entrada de visualização usando as macros $T1, $T2 e assim por diante. Para localizar exemplos dessas macros, confira os arquivos .natvis que acompanham o Visual Studio.

Correspondência de tipo do 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 tipo BaseClass:

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

Atributo de prioridade

O atributo opcional Priority especifica a ordem na qual se deve usar as definições alternativas, caso uma definição não seja analisada. Os valores possíveis do Priority são: Low, MediumLow, Medium, MediumHigh e High. O valor padrão é Medium. O atributo Priority distingue apenas entre as prioridades dentro do mesmo arquivo .natvis.

O exemplo a seguir analisa primeiro a entrada que corresponde ao 2015 STL. Se isso não for analisado, a entrada alternativa será usada 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 Type. No tipo a seguir, [State] não é opcional, mas [Exception] é. Se MyNamespace::MyClass tiver um campo chamado _M_exceptionHolder, o nó [State] e o nó [Exception] serão exibidos, mas se não houver 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 Condition

O atributo opcional Condition está disponível para muitos elementos de visualização e especifica quando se deve usar uma regra de visualização. Se a expressão dentro do atributo de condição for resolvida como false, a regra de visualização não se aplicará. Se isso for avaliado como true ou se não houver atributo Condition, a visualização se aplicará. Você pode usar esse atributo para obter 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 como true, de modo que o formulário seja exibido. Quando o membro _Myptr não está vazio, a condição é avaliada como false e 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 os elementos a serem exibidos em exibições específicas. Por exemplo, na seguinte especificação do Natvis de std::vector, o modo de exibição simple não mostra 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 nos tipos e nos membros individuais.

Elemento Version

O elemento Version define o escopo de uma entrada de visualização para determinado módulo e versão. 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 controle de versão será exibida somente quando o tipo estiver na versão do módulo especificada.

No exemplo a seguir, a visualização só será aplicável ao tipo DirectUI::Border encontrado no Windows.UI.Xaml.dll da versão 1.0 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 ambos, 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. Nomes de caminho não são permitidos.

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 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 entre chaves, portanto, os valores são avaliados. O exemplo também mostra como você pode evitar uma chave usando duas chaves ({{ ou }}).

Observação

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, considerando 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 selecionar Visualizador de Texto para exibir a cadeia de caracteres para a qual m_pszData aponta.

Dados do CStringT com o visualizador do StringView

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

Elemento Expand

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 os campos top, left, right e 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 é semelhante a este exemplo:

CRect com a expansão do elemento de item

O depurador avalia as expressões especificadas nos elementos Width e Height e mostra os valores na coluna Valor 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.

Observação

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

Expansão de 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 de 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 ArrayItems

O nó ArrayItems deve 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 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á na ordem row-major ou column-major.
  • Rank especifica a classificação da matriz.
  • O elemento Size aceita o parâmetro implícito $i, que ele substitui pelo índice de dimensão para descobrir o tamanho da matriz na dimensão.
    • No exemplo anterior, a expressão _M_extent.M_base[0] deve fornecer o comprimento da dimensão 0, _M_extent._M_base[1] da 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 implícito $i. 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 começam em 0. No entanto, se o limite inferior for ($i == 1) ? 1000 : 100, a dimensão 0 começará em 100 e a primeira dimensão começará em 1000.
      • , como [100, 1000], [100, 1001], [100, 1002], ... [101, 1000], [101, 1001],...

Um objeto bidimensional Concurrency::array na janela do depurador é assim:

Matriz bidimensional com expansão ArrayItems

Expansão de IndexListItems

Você pode usar a expansão ArrayItems somente se os elementos da matriz estiverem dispostos de forma contígua 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.

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 de 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 faz referência 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, e 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ê grave uma lógica personalizada para percorrer uma estrutura de dados, como uma tabela de hash. Use CustomListItems para visualizar as 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 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 de 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, RightPointer e ValueNode são avaliados no contexto da classe de nó de árvore. ValueNode pode ser deixado vazio ou usar this para se referir ao próprio nó TreeItems.

Expansão de 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:

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

Para ver os valores do vetor, faça uma pesquisa detalhada de dois níveis na janela variável que passa pelo membro _Myptr. Adicionando um elemento ExpandedItem, você pode eliminar a variável _Myptr da hierarquia e exibir diretamente os elementos do 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 ExpandedItem

O exemplo a seguir mostra como agregar propriedades da classe base em uma classe derivada. Suponha que a classe CPanel seja derivada de CFrameworkElement. Em vez de repetir as propriedades provenientes da classe base CFrameworkElement, a visualização do nó ExpandedItem acrescenta essas propriedades à lista filho 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 fará com que a visualização CPanel seja aplicada novamente porque o tipo de visualização padrão correspondente às regras a considera 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

Enquanto o elemento ExpandedItem fornece uma exibição de dados mais simples eliminando as 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ó de Synthetic para mostrar uma mensagem de diagnóstico para o 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 a expansão do elemento sintético

Expansão intrínseca

Uma função intrínseca personalizada que pode ser chamada por meio de uma expressão. Um elemento <Intrinsic> precisa ser acompanhado por um componente de depurador que implementa a função pela interface IDkmIntrinsicFunctionEvaluator140.

<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 o 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 do plug-in, como o nome, o identificador global exclusivo (GUID) do serviço exposto e os tipos que ele pode visualizar.

Veja 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 um nome de visualizador a ser exibido na lista suspensa ao lado do ícone de lupa no depurador. Por exemplo:

    Menu de atalho do UIVisualizer

Cada tipo definido no arquivo .natvis deve listar explicitamente os visualizadores da interface do usuário que podem 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 referencia o 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 um UIVisualizer na extensão de Inspeção de Imagem usada para exibir bitmaps na memória.

Elemento CustomVisualizer

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

Dá muito mais trabalho gravar um visualizador personalizado do que uma definição do Natvis XML, mas você não tem restrições sobre o que é compatível com o Natvis. Os 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 do Natvis funcionam 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 os tipos primitivos. Nesse cenário, uma opção é usar o especificador de formato apropriado para o 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.