Creare visualizzazioni personalizzate di oggetti C++ nel debugger usando il framework Natvis
Il framework di Natvis di Visual Studio
Natvis sostituisce il file autoexp.dat nelle versioni precedenti di Visual Studio con la sintassi XML, una diagnostica migliore, il controllo delle versioni e il supporto di più file.
Nota
Le personalizzazioni Natvis funzionano con classi e struct, ma non con typedef.
Visualizzazioni Natvis
Si usa il framework Natvis per creare regole di visualizzazione per i tipi creati, in modo che gli sviluppatori possano visualizzarli più facilmente durante il debug.
Ad esempio, la figura seguente mostra una variabile di tipo Windows::UI::XAML::Controls::TextBox in una finestra del debugger senza alcuna visualizzazione personalizzata applicata.
La riga evidenziata mostra la proprietà Text
della classe TextBox
. La gerarchia di classi complesse rende difficile trovare questa proprietà. Il debugger non sa come interpretare il tipo di stringa personalizzato, quindi non è possibile visualizzare la stringa contenuta nella casella di testo.
Lo stesso TextBox
sembra molto più semplice nella finestra delle variabili quando vengono applicate regole del visualizzatore personalizzato Natvis. I membri importanti della classe appaiono insieme, e il debugger mostra il valore testuale di base del tipo stringa personalizzato.
Usare i file natvis nei progetti C++
Natvis usa file natvis per specificare le regole di visualizzazione. Un file .natvis è un file di tipo XML con un'estensione .natvis . Lo schema Natvis viene definito nella cartella di installazione di VS <>\Xml\Schemas\1033\natvis.xsd.
La struttura di base di un file .natvis è costituita da uno o più elementi Type
che rappresentano le voci di visualizzazione. Il nome completo di ogni elemento Type
viene specificato nel relativo attributo Name
.
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="MyNamespace::CFoo">
.
.
</Type>
<Type Name="...">
.
.
</Type>
</AutoVisualizer>
Visual Studio fornisce alcuni file natvis
Aggiungere un file natvis a un progetto C++
È possibile aggiungere un file natvis a qualsiasi progetto C++.
Per aggiungere un nuovo file .natvis:
Selezionare il nodo del progetto C++ in Esplora soluzioni e selezionare Progetto>Aggiungi nuovo elementooppure fare clic con il pulsante destro del mouse sul progetto e selezionare Aggiungi>Nuovo elemento.
Se non vengono visualizzati tutti i modelli di elemento, scegliere Mostra tutti i modelli.
Nella finestra di dialogo Aggiungi nuovo elemento
selezionare Utilità File di visualizzazione debugger (natvis) .Assegnare un nome al file e selezionare Aggiungi.
Il nuovo file viene aggiunto a Esplora soluzionie viene aperto nel riquadro del documento di Visual Studio.
Il debugger di Visual Studio carica automaticamente
Per escludere un file .natvis da un .pdb:
Selezionare il file .natvis in Esplora soluzioni e fare clic sull'icona delle Proprietà oppure fare clic con il pulsante destro del mouse sul file e scegliere Proprietà.
Nell'elenco a discesa, fare clic sulla freccia accanto a Esclusi dalla compilazione e scegliere Sì, quindi fare clic su OK.
Nota
Per eseguire il debug di progetti eseguibili, utilizzare gli elementi della soluzione per aggiungere tutti i file .natvis che non si trovano nel .pdb, poiché non è disponibile alcun progetto C++.
Nota
Le regole Natvis caricate da un .pdb si applicano solo ai tipi nei moduli a cui fa riferimento il .pdb. Ad esempio, se Module1.pdb ha una voce Natvis per un tipo denominato Test
, essa si applica solo alla classe Test
in Module1.dll. Se anche un altro modulo definisce una classe denominata Test
, la voce Natvis Module1.pdb non si applica.
Per installare e registrare un file .natvis tramite un pacchetto VSIX:
Un pacchetto VSIX può installare e registrare file natvis. Indipendentemente dalla posizione in cui sono installati, tutti i file con estensione natvis registrati vengono prelevati automaticamente durante il debug.
Includere il file .natvis nel pacchetto VSIX. Ad esempio, per il file di progetto seguente:
<?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>
Registrare il file .natvis nel file 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>
Percorsi dei file Natvis
È possibile aggiungere i file .natvis alla directory utente o a una directory di sistema, se si desidera applicarli a più progetti.
I file .natvis vengono valutati nell'ordine seguente:
Qualsiasi file natvis incorporati in un file con estensione pdb si sta eseguendo il debug, a meno che non esista un file con lo stesso nome nel progetto caricato.
Qualsiasi file .natvis presente in un progetto C++ caricato o in una soluzione principale. Questo gruppo include tutti i progetti C++ caricati, incluse le librerie di classi, ma non i progetti in altri linguaggi.
Qualsiasi file .natvis installato e registrato tramite un pacchetto VSIX.
- La directory Natvis specifica dell'utente (ad esempio, %USERPROFILE%\Documents\Visual Studio 2022\Visualizers).
- La directory Natvis specifica per l'utente (ad esempio, %USERPROFILE%\Documents\Visual Studio 2019\Visualizers).
- Directory Natvis a livello di sistema (<cartella di installazione di Microsoft Visual Studio>\Common7\Packages\Debugger\Visualizers). Questa directory include i file .natvis installati con Visual Studio. Se si dispone delle autorizzazioni di amministratore, è possibile aggiungere file a questa directory.
Modificare i file .natvis durante il debug
È possibile modificare un file natvis nell'IDE durante il debug del progetto. Aprire il file nella stessa istanza di Visual Studio con cui si esegue il debug, modificarlo e salvarlo. Non appena viene salvato il file, le finestre Osserva e Variabili locali si aggiornano per riflettere la modifica.
È anche possibile aggiungere o eliminare file natvis in una soluzione di cui si sta eseguendo il debug e Visual Studio aggiunge o rimuove le visualizzazioni pertinenti.
Non è possibile aggiornare file natvis incorporati in file con estensione pdb durante il debug.
Se si modifica il file .natvis all'esterno di Visual Studio, le modifiche non hanno effetto automaticamente. Per aggiornare le finestre del debugger, è possibile rivalutare il comando
Usare anche il comando natvisreload per aggiornare il file natvis a una versione più recente. Ad esempio, il file .natvis potrebbe essere archiviato nel controllo del codice sorgente e si vogliono raccogliere le modifiche recenti apportate da qualcun altro.
Espressioni e formattazione
Le visualizzazioni Natvis usano espressioni C++ per specificare gli elementi di dati da visualizzare. Oltre ai miglioramenti e alle limitazioni delle espressioni C++ nel debugger, descritte in operatore Context (C++), tenere presente quanto segue:
Le espressioni Natvis vengono valutate nel contesto dell'oggetto visualizzato, non nel frame di stack corrente. Ad esempio,
x
in un'espressione Natvis fa riferimento al campo denominato x nell'oggetto visualizzato, non a una variabile locale denominata x nella funzione corrente. Non è possibile accedere alle variabili locali nelle espressioni Natvis, anche se è possibile accedere alle variabili globali.Le espressioni Natvis non consentono la valutazione della funzione o gli effetti collaterali. Le invocazioni di funzione e gli operatori di assegnazione vengono ignorati. Poiché le funzioni intrinseche del debugger sono prive di effetti collaterali, possono essere chiamate liberamente da qualsiasi espressione Natvis, anche se altre chiamate di funzione non sono permesse.
Per controllare la modalità di visualizzazione di un'espressione, è possibile usare uno degli identificatori di formato descritti in Identificatori di formato in C++. Gli specificatori di formato vengono ignorati quando la voce è utilizzata internamente da Natvis, come l'espressione
Size
in un'espansione ArrayItems .
Nota
Poiché il documento Natvis è XML, le espressioni non possono utilizzare direttamente il simbolo &, il maggiore di, il minore di, o gli operatori di shift. È necessario eseguire l'escape di questi caratteri sia nel corpo dell'elemento sia nelle dichiarazioni condizionali. Per esempio:
\<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\>
Visualizzazioni Natvis
È possibile definire diverse visualizzazioni Natvis per mostrare i tipi in vari modi. Ecco ad esempio una visualizzazione di std::vector
che definisce una visualizzazione semplificata denominata simple
. I DisplayString
e gli elementi ArrayItems
vengono visualizzati nella visualizzazione predefinita e nella visualizzazione simple
, mentre gli elementi [size]
e [capacity]
non vengono visualizzati nella visualizzazione 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>
Nella finestra di controllo , usa lo specificatore di formato ,view per specificare una visualizzazione alternativa. La visualizzazione semplice viene visualizzata come vec,view(simple):
Errori Natvis
Quando il debugger incontra errori in un elemento di visualizzazione, li ignora. Visualizza il tipo nel formato non elaborato oppure seleziona un'altra visualizzazione appropriata. È possibile usare la diagnostica Natvis per comprendere il motivo per cui il debugger ha ignorato una visualizzazione e per visualizzare gli errori di sintassi e di parsing sottostanti.
Per attivare la diagnostica Natvis:
- In Tools>Options (o Debug>opzioni) >Debugging>Finestra di output, impostare messaggi diagnostici Natvis (solo C++) su Errore, Avviso, o Dettagliatoe quindi selezionare OK.
Gli errori vengono visualizzati nella finestra Output.
Informazioni di riferimento sulla sintassi Natvis
Gli elementi e gli attributi seguenti possono essere usati nel file Natvis.
Elemento AutoVisualizer
L'elemento AutoVisualizer
è il nodo radice del file .natvis e contiene l'attributo dello spazio dei nomi xmlns:
.
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
.
.
</AutoVisualizer>
L'elemento AutoVisualizer
può avere Type, HResult, UIVisualizere CustomVisualizer come elementi figlio.
Tipo di elemento
Un Type
di base è simile all'esempio seguente:
<Type Name="[fully qualified type name]">
<DisplayString Condition="[Boolean expression]">[Display value]</DisplayString>
<Expand>
...
</Expand>
</Type>
L'elemento Type
specifica:
Tipo per cui usare la visualizzazione (attributo
Name
).Come dovrebbe apparire il valore di un oggetto di tale tipo (elemento
DisplayString
).Aspetto dei membri del tipo quando l'utente espande il tipo in una finestra di variabile (il nodo
Expand
).
Classi modello
L'attributo Name
dell'elemento Type
accetta un asterisco *
come carattere jolly che può essere usato per i nomi di classe template.
Nell'esempio seguente viene usata la stessa visualizzazione se l'oggetto è un CAtlArray<int>
o un CAtlArray<float>
. Se è presente una voce di visualizzazione specifica per un CAtlArray<float>
, ha la precedenza su quella generica.
<Type Name="ATL::CAtlArray<*>">
<DisplayString>{{Count = {m_nSize}}}</DisplayString>
</Type>
È possibile fare riferimento ai parametri del modello nella voce di visualizzazione usando macro $T 1, $T 2 e così via. Per trovare esempi di queste macro, vedere i file .natvis inclusi in Visual Studio.
Tipo del visualizzatore corrispondente
Se una voce di visualizzazione non viene convalidata, si utilizza la prossima visualizzazione disponibile.
Attributo ereditabile
L'attributo facoltativo Inheritable
specifica se una visualizzazione si applica solo a un tipo di base o a un tipo di base e a tutti i tipi derivati. Il valore predefinito di Inheritable
è true
.
Nell'esempio seguente la visualizzazione si applica solo al tipo di BaseClass
:
<Type Name="Namespace::BaseClass" Inheritable="false">
<DisplayString>{{Count = {m_nSize}}}</DisplayString>
</Type>
Attributo Priorità
L'attributo facoltativo Priority
specifica l'ordine in cui usare definizioni alternative, se una definizione non riesce ad analizzare. I valori possibili di Priority
sono: Low
, MediumLow
,Medium
, MediumHigh
e High
. Il valore predefinito è Medium
. L'attributo Priority
distingue solo tra le priorità all'interno dello stesso file natvis.
Nell'esempio seguente viene prima analizzata la voce che corrisponde allo STL 2015. Se l'analisi non riesce, utilizza l'entrata alternativa per la versione 2013 dello 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>
Attributo facoltativo
È possibile inserire un attributo Optional
in qualsiasi nodo. Se una sottoespressione all'interno di un nodo facoltativo non viene interpretata, il debugger ignora quel nodo ma applica il resto delle regole di Type
. Nel tipo seguente [State]
non è facoltativo, ma [Exception]
è facoltativo. Se MyNamespace::MyClass
ha un campo denominato _M_exceptionHolder
, vengono visualizzati sia il nodo [State]
che il nodo [Exception]
, ma se non è presente alcun campo _M_exceptionHolder
, viene visualizzato solo il nodo [State]
.
<Type Name="MyNamespace::MyClass">
<Expand>
<Item Name="[State]">_M_State</Item>
<Item Name="[Exception]" Optional="true">_M_exceptionHolder</Item>
</Expand>
</Type>
Attributo condizione
L'attributo facoltativo Condition
è disponibile per molti elementi di visualizzazione e specifica quando usare una regola di visualizzazione. Se l'espressione all'interno dell'attributo della condizione viene risolta in false
, la regola di visualizzazione non viene applicata. Se il risultato è true
, o se non esiste alcun attributo Condition
, la visualizzazione viene applicata. È possibile usare questo attributo per la logica if-else nelle voci di visualizzazione.
Ad esempio, la visualizzazione seguente include due elementi DisplayString
per un tipo di puntatore intelligente. Quando il membro _Myptr
è vuoto, la condizione del primo elemento DisplayString
risulta essere true
, così da visualizzare il modulo. Quando il membro _Myptr
non è vuoto, la condizione è valutata come false
e viene visualizzato il secondo elemento DisplayString
.
<Type Name="std::auto_ptr<*>">
<DisplayString Condition="_Myptr == 0">empty</DisplayString>
<DisplayString>auto_ptr {*_Myptr}</DisplayString>
<Expand>
<ExpandedItem>_Myptr</ExpandedItem>
</Expand>
</Type>
Attributi IncludeView ed ExcludeView
Gli attributi IncludeView
e ExcludeView
specificano gli elementi da visualizzare o non visualizzare in visualizzazioni specifiche. Ad esempio, nella specifica Natvis seguente di std::vector
, la visualizzazione simple
non mostra gli elementi [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>
È possibile usare gli attributi IncludeView
e ExcludeView
sui tipi e sui singoli membri.
Elemento Version
L'elemento Version
delimita un'entrata di visualizzazione a un modulo e una versione specifici. L'elemento Version
aiuta a evitare conflitti di nomi, riduce le incongruenze accidentali e permette diverse visualizzazioni per diverse versioni di tipo.
Se un file di intestazione comune usato da moduli diversi definisce un tipo, la visualizzazione con controllo delle versioni viene visualizzata solo quando il tipo si trova nella versione del modulo specificata.
Nell'esempio seguente la visualizzazione è applicabile solo per il tipo di DirectUI::Border
trovato nella Windows.UI.Xaml.dll
dalla versione 1.0 alla 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>
Non sono necessari sia Min
che Max
. Sono attributi facoltativi. Non sono supportati caratteri jolly.
L'attributo Name
è nel formato filename.ext, ad esempio hello.exe o some.dll. Non sono consentiti nomi di percorso.
Elemento DisplayString
L'elemento DisplayString
specifica una stringa da visualizzare come valore di una variabile. Accetta stringhe arbitrarie miste con espressioni. Tutti gli elementi all'interno di parentesi graffe sono interpretati come un'espressione. Ad esempio, la seguente voce DisplayString
:
<Type Name="CPoint">
<DisplayString>{{x={x} y={y}}}</DisplayString>
</Type>
Ciò significa che le variabili di tipo CPoint
appaiono come illustrato in questo esempio.
Nell'espressione DisplayString
, x
e y
, che sono membri di CPoint
, sono all'interno di parentesi graffe, quindi i relativi valori vengono valutati. L'esempio mostra anche come eseguire l'escape di una parentesi graffa usando parentesi graffe doppie ( {{
o }}
).
Nota
L'elemento DisplayString
è l'unico che accetta stringhe arbitrarie e utilizza la sintassi delle parentesi graffe. Tutti gli altri elementi di visualizzazione accettano solo espressioni che il debugger può valutare.
Elemento StringView
L'elemento StringView
definisce un valore che il debugger può inviare al visualizzatore di testo predefinito. Ad esempio, data la visualizzazione seguente per il tipo ATL::CStringT
:
<Type Name="ATL::CStringT<wchar_t,*>">
<DisplayString>{m_pszData,su}</DisplayString>
</Type>
L'oggetto CStringT
viene visualizzato in una finestra di variabile simile all'esempio seguente:
L'aggiunta di un elemento StringView
indica al debugger che può visualizzare il valore come visualizzazione di testo.
<Type Name="ATL::CStringT<wchar_t,*>">
<DisplayString>{m_pszData,su}</DisplayString>
<StringView>m_pszData,su</StringView>
</Type>
Durante il debug, è possibile selezionare l'icona della lente di ingrandimento accanto alla variabile e quindi selezionare visualizzatore di testo per visualizzare la stringa a cui m_pszData punta.
L'espressione {m_pszData,su}
include un identificatore di formato C++ su, per visualizzare il valore come stringa Unicode. Per altre informazioni, vedere identificatori di formato in C++.
Espandi elemento
Il nodo facoltativo Expand
personalizza gli elementi figlio di un tipo visualizzato quando si espande il tipo in una finestra delle variabili. Il nodo Expand
accetta un elenco di nodi figlio che specificano gli elementi figli.
Se un nodo
Expand
non è specificato in un elemento di visualizzazione, i figli usano le regole di espansione predefinite.Se viene specificato un nodo
Expand
senza nodi figlio al suo interno, il tipo non è espandibile nelle finestre del debugger.
Espansione dell'elemento
L'elemento Item
è l'elemento più semplice e comune in un nodo Expand
.
Item
definisce un unico elemento figlio. Ad esempio, una classe CRect
con campi top
, left
, right
e bottom
ha la voce di visualizzazione seguente:
<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>
Nella finestra del debugger il tipo di CRect
è simile all'esempio seguente:
Il debugger valuta le espressioni specificate negli elementi
Il debugger crea automaticamente il nodo [Visualizzazione non elaborata] per ogni espansione personalizzata. Lo screenshot precedente mostra il [Visualizzazione non elaborata] nodo espanso, per mostrare come la visualizzazione non elaborata predefinita dell'oggetto differisce dalla visualizzazione Natvis. L'espansione predefinita crea un sottoalbero per la classe base ed elenca tutti i membri dati della classe base come elementi figlio.
Nota
Se l'espressione dell'elemento item punta a un tipo complesso, il nodo Item è espandibile.
Espansione di elementi dell'array
Usare il nodo ArrayItems
per fare in modo che il debugger di Visual Studio interpreti il tipo come matrice e visualizzi i singoli elementi. La visualizzazione per std::vector
è un buon esempio:
<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>
Un std::vector
mostra i suoi singoli elementi quando viene espanso nella finestra delle variabili.
Il nodo ArrayItems
deve avere:
- Espressione
Size
(che deve restituire un numero intero) per consentire al debugger di comprendere la lunghezza della matrice. - Espressione
ValuePointer
che punta al primo elemento ( che deve essere un puntatore di un tipo di elemento che non èvoid*
).
Il valore predefinito del limite inferiore della matrice è 0. Per eseguire l'override del valore, usare un elemento LowerBound
. I file .natvis forniti con Visual Studio includono esempi.
Nota
È possibile usare l'operatore []
, ad esempio vector[i]
, con qualsiasi visualizzazione di matrice unidimensionale che usa ArrayItems
, anche se il tipo stesso (ad esempio CATLArray
) non consente questo operatore.
È anche possibile specificare matrici multidimensionali. In tal caso, il debugger necessita di leggermente più informazioni per visualizzare correttamente gli elementi figlio:
<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
specifica se la matrice è ordinata per righe o per colonne. -
Rank
specifica il rango della matrice. - L'elemento
Size
accetta il parametro implicito$i
, che sostituisce con l'indice della dimensione per trovare la lunghezza della matrice in tale dimensione.- Nell'esempio precedente l'espressione
_M_extent.M_base[0]
deve assegnare la lunghezza della dimensione 0,_M_extent._M_base[1]
la prima e così via.
- Nell'esempio precedente l'espressione
- Il
LowerBound
specifica il limite inferiore di ogni dimensione della matrice. Per le matrici multidimensionali, è possibile specificare un'espressione che usa il parametro implicito$i
. Il parametro$i
verrà sostituito con l'indice della dimensione per trovare il limite inferiore della matrice in tale dimensione.- Nell'esempio precedente tutte le dimensioni inizieranno a 0. Tuttavia, se avevi
($i == 1) ? 1000 : 100
come limite inferiore, alla dimensione 0 si inizierà a 100 e alla prima dimensione si inizierà a 1000.- , ad esempio
[100, 1000], [100, 1001], [100, 1002], ... [101, 1000], [101, 1001],...
- , ad esempio
- Nell'esempio precedente tutte le dimensioni inizieranno a 0. Tuttavia, se avevi
Ecco come viene visualizzato un oggetto Concurrency::array
bidimensionale nella finestra del debugger:
Espansione di "IndexListItems"
È possibile usare l'espansione ArrayItems
solo se gli elementi della matrice sono disposti in modo contiguo in memoria. Il debugger passa all'elemento successivo semplicemente incrementando il puntatore. Se è necessario modificare l'indice al nodo valore, usare i nodi IndexListItems
. Ecco una visualizzazione con un nodo 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>
L'unica differenza tra ArrayItems
e IndexListItems
è la ValueNode
, che prevede l'espressione completa all'elemento ith con il parametro $i
implicito.
Nota
È possibile usare l'operatore []
, ad esempio vector[i]
, con qualsiasi visualizzazione di matrice unidimensionale che usa IndexListItems
, anche se il tipo stesso (ad esempio CATLArray
) non consente questo operatore.
Espansione di LinkedListItems
Se il tipo visualizzato rappresenta un elenco collegato, il debugger può visualizzare i relativi elementi figlio usando un nodo LinkedListItems
. La visualizzazione seguente per il tipo di 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>
L'elemento Size
fa riferimento alla lunghezza dell'elenco.
HeadPointer
punta al primo elemento, NextPointer
fa riferimento all'elemento successivo e ValueNode
fa riferimento al valore dell'elemento.
Il debugger valuta le espressioni NextPointer
e ValueNode
nel contesto dell'elemento nodo LinkedListItems
, non il tipo di elenco padre. Nell'esempio precedente CAtlList
ha una classe CNode
(disponibile in atlcoll.h
) che è un nodo dell'elenco collegato.
m_pNext
e m_element
sono campi della classe CNode
, non della classe CAtlList
.
ValueNode
può essere lasciato vuoto oppure usare this
per fare riferimento al nodo LinkedListItems
stesso.
Espansione di CustomListItems
L'espansione CustomListItems
consente di scrivere logica personalizzata per attraversare una struttura di dati, ad esempio una tabella hash. Utilizzare CustomListItems
per visualizzare le strutture di dati che possono usare espressioni C++ per tutto ciò che è necessario valutare, ma non seguono esattamente il modello per ArrayItems
, IndexListItems
, o LinkedListItems
.
È possibile usare Exec
per eseguire il codice all'interno di un'espansione CustomListItems
, usando le variabili e gli oggetti definiti nell'espansione. È possibile usare operatori logici, operatori aritmetici e operatori di assegnazione con Exec
. Non è possibile usare Exec
per valutare le funzioni, ad eccezione delle funzioni intrinseche debugger supportate dall'analizzatore di espressioni C++.
Il visualizzatore seguente per CAtlMap
è un esempio eccellente in cui CustomListItems
è appropriato.
<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>
TreeItems in espansione
Se il tipo visualizzato rappresenta un albero, il debugger può esplorare l'albero e visualizzarne i nodi figli usando un nodo TreeItems
. Ecco la visualizzazione per il tipo std::map
usando un nodo 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>
La sintassi è simile al nodo LinkedListItems
.
LeftPointer
, RightPointer
e ValueNode
vengono valutati nel contesto della classe del nodo dell'albero.
ValueNode
può essere lasciato vuoto o usare this
per fare riferimento al nodo TreeItems
stesso.
Espansione dell'Elemento Espanso
L'elemento ExpandedItem
genera una visualizzazione figlio aggregata visualizzando proprietà di classi di base o membri dati come se fossero elementi figlio del tipo visualizzato. Il debugger valuta l'espressione specificata e aggiunge i nodi figlio del risultato all'elenco figlio del tipo visualizzato.
Ad esempio, il tipo di puntatore intelligente auto_ptr<vector<int>>
in genere viene visualizzato come:
di espansione predefinita
Per visualizzare i valori del vettore, è necessario eseguire il drill-down di due livelli nella finestra delle variabili, passando attraverso il membro _Myptr
. Aggiungendo un elemento ExpandedItem
, è possibile eliminare la variabile _Myptr
dalla gerarchia e visualizzare direttamente gli elementi vettoriali:
<Type Name="std::auto_ptr<*>">
<DisplayString>auto_ptr {*_Myptr}</DisplayString>
<Expand>
<ExpandedItem>_Myptr</ExpandedItem>
</Expand>
</Type>
Nell'esempio seguente viene illustrato come aggregare le proprietà della classe base in una classe derivata. Si supponga che la classe CPanel
derivi da CFrameworkElement
. Anziché ripetere le proprietà provenienti dalla classe base CFrameworkElement
, la visualizzazione del nodo ExpandedItem
accoda tali proprietà all'elenco figlio della 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>
Il specificatore di formato e, che disattiva la corrispondenza della visualizzazione per la classe derivata, è necessario qui. In caso contrario, l'espressione *(CFrameworkElement*)this
causerebbe nuovamente l'applicazione della visualizzazione CPanel
, perché le regole predefinite di corrispondenza del tipo di visualizzazione lo considerano quello più appropriato. Usare l'identificatore di formato nd per indicare al debugger di usare la visualizzazione della classe di base o l'espansione predefinita se la classe di base non ha alcuna visualizzazione.
Espansione degli elementi sintetici
Mentre l'elemento ExpandedItem
fornisce una visualizzazione più semplice dei dati eliminando le gerarchie, il nodo Synthetic
fa l'opposto. Consente di creare un elemento figlio artificiale che non è un risultato di un'espressione. L'elemento artificiale può avere propri elementi figli. Nell'esempio seguente la visualizzazione per il tipo di Concurrency::array
usa un nodo Synthetic
per visualizzare un messaggio di diagnostica all'utente:
<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>
Espansione intrinseca
Funzione intrinseca personalizzata che può essere chiamata da un'espressione. Un elemento <Intrinsic>
deve essere accompagnato da un componente del debugger che implementa la funzione tramite l'interfaccia 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
L'elemento HResult
consente di personalizzare le informazioni mostrate per un HRESULT nelle finestre del debugger. L'elemento HRValue
deve contenere il valore a 32 bit dell'HRESULT da personalizzare. L'elemento HRDescription
contiene le informazioni da visualizzare nella finestra del debugger.
<HResult Name="MY_E_COLLECTION_NOELEMENTS">
<HRValue>0xABC0123</HRValue>
<HRDescription>No elements in the collection.</HRDescription>
</HResult>
Elemento UIVisualizer
Un elemento UIVisualizer
registra un plug-in visualizzatore grafico con il debugger. Un visualizzatore grafico crea una finestra di dialogo o un'altra interfaccia che mostra una variabile o un oggetto in modo coerente con il relativo tipo di dati. Il plug-in del visualizzatore deve essere creato come VSPackagee deve esporre un servizio che il debugger può utilizzare. Il file .natvis contiene informazioni di registrazione per il plug-in, come il suo nome, l'identificatore univoco globale (GUID) del servizio esposto e i tipi che può visualizzare.
Ecco un esempio di 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>
Una coppia di attributi
ServiceId
-Id
identifica unUIVisualizer
. IlServiceId
è il GUID del servizio esposto dal pacchetto del visualizzatore.Id
è un identificatore univoco che differenzia i visualizzatori, se un servizio fornisce più di uno. Nell'esempio precedente lo stesso servizio visualizzatore fornisce due visualizzatori.L'attributo
MenuName
definisce un nome del visualizzatore da visualizzare nell'elenco a discesa accanto all'icona della lente di ingrandimento nel debugger. Per esempio:
Ogni tipo definito nel file natvis deve elencare in modo esplicito tutti i visualizzatori dell'interfaccia utente che possono visualizzarlo. Il debugger associa i riferimenti del visualizzatore nei tipi definiti con i visualizzatori registrati. Ad esempio, la voce di tipo seguente per std::vector
fa riferimento al UIVisualizer
nell'esempio precedente.
<Type Name="std::vector<int,*>">
<UIVisualizer ServiceId="{5452AFEA-3DF6-46BB-9177-C0B08F318025}" Id="1" />
</Type>
È possibile visualizzare un esempio di
Elemento CustomVisualizer
CustomVisualizer
è un punto di estendibilità che specifica un'estensione VSIX scritta per controllare le visualizzazioni in Visual Studio Code. Per altre informazioni sulla scrittura di estensioni VSIX, vedere Visual Studio SDK.
È molto più importante scrivere un visualizzatore personalizzato rispetto a una definizione Natvis XML, ma è possibile evitare vincoli relativi a ciò che Natvis esegue o non supporta. I visualizzatori personalizzati hanno accesso al set completo di API di estendibilità del debugger, che possono eseguire query e modificare il processo di debug o comunicare con altre parti di Visual Studio.
È possibile usare gli attributi Condition
, IncludeView
e ExcludeView
sugli elementi CustomVisualizer
.
Limitazioni
Le personalizzazioni Natvis funzionano con classi e strutture, ma non con typedef.
Natvis non supporta i visualizzatori per i tipi primitivi (ad esempio, int
, bool
) o per i puntatori ai tipi primitivi. In questo scenario, un'opzione consiste nell'usare l'identificatore di formato appropriato per il caso d'uso. Ad esempio, se si usa double* mydoublearray
nel codice, è possibile utilizzare uno specificatore di formato di array nella finestra di controllo del debugger, come l'espressione mydoublearray, [100]
, che mostra i primi 100 elementi.