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.
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.
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:
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.
Na caixa de diálogo Adicionar Novo Item, selecione Visual C++>Utilitário>Arquivo de visualização do depurador (.natvis).
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:
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.
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.
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>
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:
Os arquivos .natvis inseridos em um .pdb que você está depurando, a menos que exista um arquivo com o mesmo nome no projeto carregado.
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.
Todos os arquivos .natvis instalados e registrados por meio de um pacote VSIX.
- O diretório do Natvis específico do usuário (por exemplo, %USERPROFILE%\Documents\Visual Studio 2022\Visualizers).
- O diretório do Natvis específico do usuário (por exemplo, %USERPROFILE%\Documents\Visual Studio 2019\Visualizers).
- 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 \>\> 24),x\</Item\>
\<Item Name="HiByteStatus" Condition="(_flags \& 0xFF000000) == 0"\>"None"\</Item\>
\<Item Name="HiByteStatus" Condition="(_flags \& 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<*>">
<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):
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:
Para qual tipo a visualização deve ser usada (o atributo
Name
).Qual deve ser a aparência de um objeto desse tipo (semelhante à do elemento
DisplayString
).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<*>">
<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<*>" Priority="MediumLow">
<DisplayString>{_Callee}</DisplayString>
<Expand>
<ExpandedItem>_Callee</ExpandedItem>
</Expand>
</Type>
<!-- VC 2015 -->
<Type Name="std::reference_wrapper<*>">
<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<*>">
<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<*>">
<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:
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<wchar_t,*>">
<DisplayString>{m_pszData,su}</DisplayString>
</Type>
O objeto CStringT
é exibido em uma janela variável como este exemplo:
Adicionar um elemento StringView
informa ao depurador que ele pode exibir o valor como uma visualização de texto.
<Type Name="ATL::CStringT<wchar_t,*>">
<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.
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:
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<*>">
<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:
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 sejavoid*
).
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<*,*>">
<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.
- No exemplo anterior, a expressão
- 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],...
- , como
- No exemplo anterior, todas as dimensões começam em 0. No entanto, se o limite inferior for
Um objeto bidimensional Concurrency::array
na janela do depurador é assim:
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<*>">
<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<*,*>">
<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
, IndexListItems
ou 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<*,*,*,*>">
<AlternativeType Name="ATL::CMapToInterface<*,*,*>"/>
<AlternativeType Name="ATL::CMapToAutoPtr<*,*,*>"/>
<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<*>">
<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:
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<*>">
<DisplayString>auto_ptr {*_Myptr}</DisplayString>
<Expand>
<ExpandedItem>_Myptr</ExpandedItem>
</Expand>
</Type>
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<*,*>">
<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>
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<*>">
<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 umUIVisualizer
. OServiceId
é 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:
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<int,*>">
<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.