Creare visualizzazioni personalizzate di oggetti C++ nel debugger usando il framework Natvis
Il framework Natvis di Visual Studio personalizza il modo in cui i tipi nativi vengono visualizzati nelle finestre delle variabili del debugger, ad esempio variabili locali e espressioni di controllo e in Dati Suggerimenti. 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
aspetto è 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 i file natvis per specificare le regole di visualizzazione. Un file natvis è un file XML con estensione natvis . Lo schema Natvis è definito nella <cartella> di installazione di VS\Xml\Schemas\1033\natvis.xsd.
La struttura di base di un file natvis è uno o più Type
elementi che rappresentano le voci di visualizzazione. Il nome completo di ogni Type
elemento viene specificato nel relativo Name
attributo.
<?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 elemento oppure fare clic con il pulsante destro del mouse sul progetto e scegliere 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 Il file di visualizzazione Debugger utilità>Visual C++>(natvis).
Assegnare un nome al file e selezionare Aggiungi.
Il nuovo file viene aggiunto a Esplora soluzioni e viene aperto nel riquadro del documento di Visual Studio.
Il debugger di Visual Studio carica automaticamente i file natvis nei progetti C++ e, per impostazione predefinita, li include anche nel file con estensione pdb quando il progetto viene compilato. Se si esegue il debug dell'app compilata, il debugger carica il file natvis dal file con estensione pdb , anche se il progetto non è aperto. Se non si vuole che il file natvis sia incluso nel file con estensione pdb, è possibile escluderlo dal file con estensione pdb compilato.
Per escludere un file natvis da un file con estensione pdb:
Selezionare il file natvis in Esplora soluzioni e selezionare l'icona Proprietà oppure fare clic con il pulsante destro del mouse sul file e scegliere Proprietà.
Nell'elenco a discesa la freccia accanto a Esclusa da compilazione selezionare Sì, quindi selezionare OK.
Nota
Per il debug di progetti eseguibili, usare gli elementi della soluzione per aggiungere eventuali file natvis che non si trovano nel file con estensione pdb, poiché non è disponibile alcun progetto C++.
Nota
Le regole Natvis caricate da un file con estensione pdb si applicano solo ai tipi nei moduli a cui fa riferimento . pdb . Ad esempio, se Module1.pdb ha una voce Natvis per un tipo denominato Test
, si applica solo alla Test
classe in Module1.dll. Se un altro modulo definisce anche una classe denominata Test
, la voce Natvis Module1.pdb non viene applicata.
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 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:
Tutti i file natvis incorporati in un file con estensione pdb di cui si esegue il debug, a meno che non esista un file con lo stesso nome nel progetto caricato.
Tutti i file natvis che si trovano in un progetto C++ caricato o in una soluzione di primo livello. 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 %U edizione Standard RPROFILE%\Documents\Visual Studio 2022\Visualizers.
- Directory Natvis specifica dell'utente, ad esempio %U edizione Standard RPROFILE%\Documents\Visual Studio 2019\Visualizers.
- Directory Natvis a livello di sistema (<cartella> di installazione di Microsoft Visual Studio\Common7\Packages\Debugger\Visualizers). Questa directory contiene 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 il file viene salvato, le finestre Espressioni di controllo e variabili locali vengono aggiornate 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 i file natvis incorporati nei file con estensione pdb durante il debug.
Se si modifica il file natvis all'esterno di Visual Studio, le modifiche non diventano effettive automaticamente. Per aggiornare le finestre del debugger, è possibile rivalutare 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 raccogliere le modifiche recenti apportate da qualcun altro.
Espressioni e formattazione
Nelle visualizzazioni Natvis si 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 di contesto (C++), tenere presente quanto segue:
Le espressioni di Natvis vengono valutate nel contesto dell'oggetto da visualizzare, non nello stack frame corrente. Ad esempio,
x
in un'espressione Natvis si riferisce 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é le 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 qualsiasi degli identificatori di formato descritti in Identificatori di formato in C++. Gli identificatori di formato vengono ignorati quando la voce viene usata internamente da Natvis, ad esempio l'espressione in un'espansione
Size
ArrayItems.
Nota
Poiché il documento Natvis è XML, le espressioni non possono usare direttamente l'e commerciale, maggiore di, minore o operatori di spostamento. È necessario eseguire l'escape di questi caratteri sia nel corpo dell'elemento che nelle istruzioni della condizione. Ad 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 di 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
. Gli DisplayString
elementi e ArrayItems
vengono visualizzati nella visualizzazione predefinita e nella simple
visualizzazione, mentre gli [size]
elementi e [capacity]
non vengono visualizzati nella simple
visualizzazione.
<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 usare l'identificatore di formato ,view 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 usare la diagnostica Natvis per comprendere il motivo per cui il debugger ha ignorato una voce di visualizzazione e per visualizzare gli errori di sintassi e analisi sottostanti.
Per attivare la diagnostica Natvis:
- In Opzioni strumenti> (o Opzioni di debug>) >Finestra di output debug>impostare i messaggi di diagnostica Natvis (solo C++) su Errore, Avviso o Dettagliato e quindi selezionare OK.
Gli errori vengono visualizzati nella finestra Output .
Riferimento per la sintassi di 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 elementi figlio Type, HResult, UIVisualizer e CustomVisualizer .
Elemento Type
Un esempio di base Type
è simile al seguente:
<Type Name="[fully qualified type name]">
<DisplayString Condition="[Boolean expression]">[Display value]</DisplayString>
<Expand>
...
</Expand>
</Type>
L'elemento Type
specifica:
Tipo per il quale deve essere usata la visualizzazione (attributo
Name
).A quale valore di un oggetto di tale tipo deve essere simile (elemento
DisplayString
).Aspetto dei membri del tipo quando l'utente espande il tipo in una finestra di variabile (il
Expand
nodo).
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 modelli.
Nell'esempio seguente viene usata la stessa visualizzazione se l'oggetto è o CAtlArray<int>
.CAtlArray<float>
Se è presente una voce di visualizzazione specifica per un CAtlArray<float>
oggetto , 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 esempi di queste macro, vedere i file NATVIS forniti con Visual Studio.
Corrispondenza del tipo di visualizzatore
Se una voce di visualizzazione non viene convalidata, viene usata la visualizzazione disponibile successiva.
Attributo Inheritable
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 BaseClass
tipo :
<Type Name="Namespace::BaseClass" Inheritable="false">
<DisplayString>{{Count = {m_nSize}}}</DisplayString>
</Type>
Attributo Priority
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 alla libreria STL 2015. Se l'analisi non riesce, usa 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 Optional
È possibile inserire un Optional
attributo 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 Type
regole. Nel tipo seguente [State]
non è facoltativo, mentre [Exception]
lo è. Se MyNamespace::MyClass
ha un campo denominato _M_exceptionHolder
, vengono visualizzati sia il [State]
nodo che il [Exception]
nodo, ma se non è presente alcun _M_exceptionHolder
campo, viene visualizzato solo il [State]
nodo.
<Type Name="MyNamespace::MyClass">
<Expand>
<Item Name="[State]">_M_State</Item>
<Item Name="[Exception]" Optional="true">_M_exceptionHolder</Item>
</Expand>
</Type>
Attributo Condition
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 è presente alcun Condition
attributo, viene applicata la visualizzazione. È possibile usare questo attributo per la logica if-else nelle voci di visualizzazione.
Ad esempio, la visualizzazione seguente include due DisplayString
elementi per un tipo di puntatore intelligente. Quando il _Myptr
membro è vuoto, la condizione del primo DisplayString
elemento viene risolta in true
, in modo che venga visualizzata la maschera. Quando il _Myptr
membro non è vuoto, la condizione restituisce false
e viene visualizzato il secondo DisplayString
elemento.
<Type Name="std::auto_ptr<*>">
<DisplayString Condition="_Myptr == 0">empty</DisplayString>
<DisplayString>auto_ptr {*_Myptr}</DisplayString>
<Expand>
<ExpandedItem>_Myptr</ExpandedItem>
</Expand>
</Type>
Attributi IncludeView e ExcludeView
Gli IncludeView
attributi e ExcludeView
specificano gli elementi da visualizzare o non visualizzare in visualizzazioni specifiche. Ad esempio, nella specifica Natvis seguente di std::vector
, la simple
visualizzazione non visualizza gli [size]
elementi 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 IncludeView
attributi e ExcludeView
sui tipi e sui singoli membri.
Elemento Version
L'elemento definisce l'ambito Version
di una voce di visualizzazione per un modulo e una versione specifici. L'elemento Version
consente di evitare conflitti di nomi, riduce le mancate corrispondenze accidentali e consente visualizzazioni diverse per versioni di tipo diverse.
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 DirectUI::Border
tipo trovato nella Windows.UI.Xaml.dll
versione 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 combinate con espressioni. Tutto ciò che è racchiuso tra parentesi graffe viene interpretato come un'espressione. Ad esempio, la voce seguente DisplayString
:
<Type Name="CPoint">
<DisplayString>{{x={x} y={y}}}</DisplayString>
</Type>
Ciò significa che le variabili di tipo CPoint
vengono visualizzate come illustrato nella figura seguente:
Nell'espressione x
e y
, che sono membri di , sono all'interno DisplayString
di CPoint
parentesi graffe, quindi i relativi valori vengono valutati. L'esempio mostra anche come è possibile eseguire l'escape di una parentesi graffa usando parentesi graffe doppie ( {{
o }}
).
Nota
L'elemento DisplayString
è l'unico elemento che accetta stringhe arbitrarie e la sintassi con 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 ATL::CStringT
tipo:
<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 StringView
elemento 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 Expand
nodo accetta un elenco di nodi figlio che definiscono gli elementi figlio.
Se un
Expand
nodo non viene specificato in una voce di visualizzazione, gli elementi figlio usano le regole di espansione predefinite.Se un
Expand
nodo viene specificato senza nodi figlio al suo interno, il tipo non è espandibile nelle finestre del debugger.
Espansione di Item
L'elemento Item
è l'elemento più semplice e comune in un Expand
nodo. Item
definisce un singolo elemento figlio. Ad esempio, una CRect
classe 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 CRect
tipo è simile all'esempio seguente:
Il debugger valuta le espressioni specificate negli Width
elementi e Height
e mostra i valori nella colonna Valore della finestra delle variabili.
Il debugger crea automaticamente il nodo [Visualizzazione non elaborata] per ogni espansione personalizzata. Lo screenshot precedente mostra il nodo [Visualizzazione non elaborata] 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 elemento punta a un tipo complesso, il nodo Item stesso è espandibile.
ArrayItems expansion
Usare il nodo ArrayItems
per consentire al debugger di Visual Studio di interpretare il tipo come una matrice e visualizzarne i singoli elementi. La visualizzazione per std::vector
costituisce un ottimo 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 elemento std::vector
visualizza i singoli elementi quando viene espanso nella finestra delle variabili:
Il ArrayItems
nodo deve avere:
- Un'espressione
Size
(che deve restituire un numero intero) per consentire al debugger di riconoscere la lunghezza della matrice. - Espressione
ValuePointer
che punta al primo elemento ( che deve essere un puntatore di un tipo di elemento che nonvoid*
è ).
Il valore predefinito del limite inferiore della matrice è 0. Per eseguire l'override del valore, usare un LowerBound
elemento . I file natvis forniti con Visual Studio includono esempi.
Nota
È possibile usare l'operatore []
, ad esempio , con qualsiasi visualizzazione di matrice unidimensionale che usa ArrayItems
, anche se il tipo stesso (ad esempio vector[i]
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 principale o principale di riga.Rank
specifica l'ordine di priorità della matrice.- L'elemento
Size
accetta il parametro$i
implicito che sostituisce con l'indice delle dimensioni per individuare la lunghezza della matrice in quella dimensione.- Nell'esempio precedente, l'espressione
_M_extent.M_base[0]
deve assegnare la lunghezza della dimensione 0,_M_extent._M_base[1]
della prima e così via.
- Nell'esempio precedente, l'espressione
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$i
parametro 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 si dispone
($i == 1) ? 1000 : 100
del limite inferiore, la 0a dimensione inizierà a 100 e la prima dimensione inizierà a 1000.- Come
[100, 1000], [100, 1001], [100, 1002], ... [101, 1000], [101, 1001],...
- Come
- Nell'esempio precedente tutte le dimensioni inizieranno a 0. Tuttavia, se si dispone
Ecco l'aspetto di un oggetto bidimensionale Concurrency::array
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 nel nodo valore, usare IndexListItems
i nodi. Ecco una visualizzazione con un IndexListItems
nodo:
<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 è , ValueNode
che prevede l'espressione completa per l'elemento i con il parametro implicito $i
IndexListItems
.
Nota
È possibile usare l'operatore []
, ad esempio , con qualsiasi visualizzazione di matrice unidimensionale che usa IndexListItems
, anche se il tipo stesso (ad esempio vector[i]
CATLArray
) non consente questo operatore.
Espansione di LinkedListItems
Se il tipo visualizzato rappresenta un elenco collegato, il debugger può visualizzarne i figli tramite un nodo LinkedListItems
. La visualizzazione seguente per il CAtlList
tipo 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
si riferisce 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 NextPointer
espressioni e ValueNode
nel contesto dell'elemento LinkedListItems
node, non il tipo di elenco padre. Nell'esempio precedente, CAtlList
ha una CNode
classe (disponibile in atlcoll.h
) che è un nodo dell'elenco collegato. m_pNext
e m_element
sono campi di tale CNode
classe, non della CAtlList
classe .
ValueNode
può essere lasciato vuoto oppure usare this
per fare riferimento al LinkedListItems
nodo stesso.
Espansione CustomListItems
L'espansione CustomListItems
consente di scrivere una logica personalizzata per attraversare una struttura dei dati, ad esempio una tabella hash. Usare CustomListItems
per visualizzare le strutture di dati che possono usare espressioni C++ per tutto ciò che è necessario valutare, ma non adattare lo stampo per ArrayItems
, IndexListItems
o LinkedListItems
.
È possibile usare Exec
per eseguire 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 del 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ò esaminare l'albero e visualizzarne i figli tramite un nodo TreeItems
. Ecco la visualizzazione per il std::map
tipo usando un TreeItems
nodo:
<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 LinkedListItems
nodo. 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 TreeItems
nodo stesso.
Espansione di ExpandedItem
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 auto_ptr<vector<int>>
puntatore intelligente viene in genere visualizzato come:
Per visualizzare i valori del vettore, è necessario eseguire il drill-down di due livelli nella finestra delle variabili, passando attraverso il _Myptr
membro. Aggiungendo un elemento ExpandedItem
, è possibile eliminare la variabile _Myptr
dalla gerarchia e visualizzare direttamente gli elementi del vettore:
<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 di base CFrameworkElement
, la ExpandedItem
visualizzazione del nodo aggiunge tali proprietà all'elenco figlio della CPanel
classe .
<Type Name="CPanel">
<DisplayString>{{Name = {*(m_pstrName)}}}</DisplayString>
<Expand>
<Item Name="IsItemsHost">(bool)m_bItemsHost</Item>
<ExpandedItem>*(CFrameworkElement*)this,nd</ExpandedItem>
</Expand>
</Type>
In questo caso è necessario l'identificatore di formato nd che disattiva la corrispondenza della visualizzazione per la classe derivata. In caso contrario, l'espressione *(CFrameworkElement*)this
provocherebbe di nuovo l'applicazione della CPanel
visualizzazione, perché le regole di corrispondenza del tipo di visualizzazione predefinite la considerano la più appropriata. 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 elementi sintetici
Il nodo ExpandedItem
esegue la funzione opposta rispetto all'elemento Synthetic
, che fornisce una visualizzazione dei dati più semplice eliminando le gerarchie. Consente di creare un elemento figlio artificiale che non è un risultato di un'espressione. L'elemento artificiale può avere elementi figlio propri. Nell'esempio seguente nella visualizzazione del tipo Concurrency::array
viene usato un nodo Synthetic
per mostrare 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 instrinsica
Funzione intrinseca personalizzata che può essere chiamata da un'espressione. Un <Intrinsic>
elemento 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 visualizzate per un HRESULT nelle finestre del debugger. L'elemento HRValue
deve contenere il valore a 32 bit di 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 del 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 VSPackage e deve esporre un servizio utilizzabile dal debugger. Il file natvis contiene informazioni di registrazione per il plug-in, ad esempio il nome, l'identificatore univoco globale (GUID) del servizio esposto e i tipi che è possibile visualizzare.
Il seguente è 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
ServiceId
-Id
coppia di attributi identifica un oggettoUIVisualizer
.ServiceId
è 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. Ad esempio:
Ogni tipo definito nel file natvis deve elencare in modo esplicito tutti i visualizzatori dell'interfaccia utente che possono visualizzarlo. Il debugger corrisponde ai riferimenti del visualizzatore nelle voci di tipo con i visualizzatori registrati. Ad esempio, la voce di tipo seguente per std::vector
fare riferimento a UIVisualizer
nell'esempio precedente.
<Type Name="std::vector<int,*>">
<UIVisualizer ServiceId="{5452AFEA-3DF6-46BB-9177-C0B08F318025}" Id="1" />
</Type>
È possibile visualizzare un esempio di nell'estensione UIVisualizer
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 Condition
attributi , IncludeView
e ExcludeView
sugli CustomVisualizer
elementi .
Limiti
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 Espressione di controllo del debugger, ad esempio l'espressione mydoublearray, [100]
, che mostra i primi 100 elementi.