Creare visualizzazioni personalizzate di oggetti C++ nel debugger usando il framework Natvis
Il framework di Natvis di Visual Studio personalizza il modo in cui i tipi nativi vengono visualizzati nelle finestre delle variabili del debugger, ad esempio le finestre variabili locali e Espressioni di controllo e in descrizioni dati. Le visualizzazioni Natvis consentono di rendere più visibili i tipi creati durante il debug.
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 vengono visualizzati insieme e il debugger mostra il valore stringa sottostante 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 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 consiste in 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 nella cartella di installazione di <VS>\Common7\Packages\Debugger\Visualizers. Questi file hanno regole di visualizzazione per molti tipi comuni e possono fungere da esempi per la scrittura di visualizzazioni per nuovi tipi.
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 Visual C++>Utilità>file di visualizzazione del debugger (.natvis).
Assegnare un nome al file e selezionare Aggiungi.
Il nuovo file viene aggiunto a Esplora soluzionie si apre nel riquadro documento di Visual Studio.
Il debugger di Visual Studio carica automaticamente file natvis nei progetti C++ e, per impostazione predefinita, li include anche nel file pdb quando il progetto viene compilato. Se si esegue il debug dell'app compilata, il debugger carica il file .natvis dal file pdb, anche se il progetto non è aperto. Se non si desidera che il file .natvis sia incluso nel .pdb, è possibile escluderlo dal file .pdb compilato.
Per escludere un .natvis file da un .pdb:
Selezionare il file .natvis in Esplora soluzionie selezionare l'icona Proprietà oppure fare clic con il pulsante destro del mouse sul file e scegliere Proprietà.
Nell'elenco a discesa, fai clic sulla freccia accanto a Escluso dalla Compilazione e seleziona Sì, quindi seleziona OK.
Nota
Per il debug di progetti eseguibili, usare 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
, si applica solo alla classe Test
in Module1.dll. Se un altro modulo definisce anche una classe denominata Test
, il Module1.pdb voce Natvis non si applica.
Per installare e registrare un file .natvis tramite un pacchetto VSIX:
Un pacchetto VSIX può installare e registrare .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 file natvis alla directory utente o a una directory di sistema, se si vuole 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 che si trova 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.
Tutti i file natvis installati e registrati tramite un pacchetto VSIX.
- Directory Natvis specifica dell'utente, ad esempio %USERPROFILE%\Documents\Visual Studio 2022\Visualizers).
- Directory Natvis specifica dell'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 che sono 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 Watch 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 vengono applicate automaticamente. Per aggiornare le finestre del debugger, è possibile rieseguire il comando natvisreload nella finestra Immediata. Le modifiche diventano quindi effettive senza riavviare la sessione di debug.
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 desidera acquisire le modifiche recenti apportate da altri.
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 dello 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 chiamate di funzione e gli operatori di assegnazione vengono ignorati. Poiché funzioni intrinseche del debugger sono prive di effetti collaterali, potrebbero essere chiamate liberamente da qualsiasi espressione Natvis, anche se altre chiamate di funzione non sono consentite.
Per controllare la modalità di visualizzazione di un'espressione, è possibile usare uno degli identificatori di formato descritti in Identificatori di formato in C++. Gli identificatori di formato vengono ignorati quando la voce è usata internamente da Natvis, come l'espressione
Size
in un'espansione di ArrayItems .
Nota
Poiché il documento Natvis è XML, le tue espressioni non possono usare direttamente il simbolo e commerciale, maggiore di, minore di o gli operatori di spostamento. È necessario eseguire l'escape di questi caratteri sia nel corpo dell'oggetto che nelle istruzioni 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 visualizzazioni Natvis diverse per visualizzare i tipi in modi diversi. 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 espressione di controllo di usare l'identificatore di formato visualizzazione per specificare una visualizzazione alternativa. La visualizzazione semplice viene visualizzata come vec,view(simple):
Errori Natvis
Quando il debugger rileva errori in una voce di visualizzazione, li ignora. Visualizza il tipo nel formato non elaborato oppure seleziona un'altra visualizzazione appropriata. È possibile utilizzare la diagnostica Natvis per comprendere il motivo per cui il debugger ha ignorato una voce di visualizzazione e per rilevare eventuali errori di sintassi e di parsing sottostanti.
Per attivare la diagnostica Natvis:
- In Tools>Options (o Opzioni>debug) >Debug>Finestra di outputimpostare messaggi di diagnostica Natvis () Solo C++) per Errore, avviso o dettagliato e 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, UIVisualizer, e 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
).Che aspetto dovrebbe avere il valore di un oggetto di quel tipo (elemento
DisplayString
).Come dovrebbero apparire i membri del tipo quando l'utente espande il tipo in una finestra delle variabili (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 basati su 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 con estensione natvis forniti con Visual Studio.
Corrispondenza dei tipi del visualizzatore
Se una voce di visualizzazione non viene convalidata, viene utilizzata la visualizzazione successiva 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 di 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 corrispondente al 2015 STL. Se l'analisi non riesce, viene utilizzata la voce alternativa per la versione 2013 di 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 non viene analizzata una sottoespressione all'interno di un nodo facoltativo, il debugger ignora tale 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 di 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 restituisce true
o non esiste alcun attributo Condition
, viene applicata la visualizzazione. È 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
viene risolta in true
, in modo che venga visualizzata la maschera. Quando il membro _Myptr
non è vuoto, la condizione restituisce 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 visualizza 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 di versione
L'elemento Version
limita l'ambito di una voce di visualizzazione a un modulo e versione specifici. L'elemento Version
consente di evitare conflitti di nomi, ridurre le mancate corrispondenze accidentali ed permette visualizzazioni differenti per versioni di tipo differenti.
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
si visualizzano come in questa illustrazione.
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 elemento che accetta stringhe arbitrarie e sintassi di parentesi graffa. 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 di 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 i figli di un tipo visualizzato quando si espande il tipo nella finestra delle variabili. Il nodo Expand
accetta un elenco di nodi figlio che definiscono gli elementi figlio.
Se un nodo
Expand
non è specificato in una voce di visualizzazione, i nodi figli usano le regole di espansione predefinite.Se viene specificato un nodo
Expand
senza nodi figli sotto di esso, 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 singolo 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 Width
e Height
e visualizza i valori nella colonna valore della finestra delle variabili.
Il debugger crea automaticamente il [Visualizzazione non elaborata] nodo 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 ArrayItems
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 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 altre 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 è in ordine 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 è stato
($i == 1) ? 1000 : 100
come limite inferiore, la 0a dimensione inizierà a 100 e la prima dimensione 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 è stato
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 contiguamente in memoria. Il debugger passa all'elemento successivo semplicemente incrementando il puntatore. Se è necessario manipolare l'indice al nodo valore, utilizzare 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 una lista collegata, il debugger può mostrarne gli elementi figli 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 degli elementi personalizzati della lista
L'espansione CustomListItems
consente di scrivere logica personalizzata per attraversare una struttura di dati, ad esempio una tabella hash. Si utilizzi CustomListItems
per visualizzare strutture di dati che possono utilizzare espressioni C++ per valutare tutto ciò di cui hai bisogno, ma che non si adattano del tutto allo schema 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>
Espansione di TreeItems
Se il tipo visualizzato rappresenta un albero, il debugger può percorrere l'albero e visualizzarne i figli usando un nodo TreeItems
. Ecco la visualizzazione per il tipo di 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 della struttura ad 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 le proprietà delle classi di base o dei membri dei dati, simulando che siano figli 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:
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>
di espansione ExpandedItem
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>
L'identificatore di formato nd, 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 di oggetti 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. Per altre informazioni sull'implementazione di una funzione intrinseca personalizzata, vedere Implementare una funzione intrinseca personalizzata natVis.
<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 visualizzate per un HRESULT nelle finestre del debugger. L'elemento HRValue
deve contenere il valore a 32 bit del 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
consente di registrare un plug-in visualizzatore grafico nel 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 nome, l'identificatore globale univoco (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 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 confronta i riferimenti ai visualizzatori nelle voci del tipo 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 UIVisualizer
nell'estensione Image Watch usata per visualizzare bitmap in memoria.
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 struct, 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 usare un identificatore di formato di matrice nella finestra Osservazione del debugger, come l'espressione mydoublearray, [100]
, che mostra i primi 100 elementi.