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.

Visualizzazione predefinita textBox

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.

Dati textBox con il visualizzatore

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 :

  1. 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.

  2. Nella finestra di dialogo Aggiungi nuovo elemento selezionare Il file di visualizzazione Debugger utilità>Visual C++>(natvis).

  3. 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:

  1. 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à.

  2. Nell'elenco a discesa la freccia accanto a Esclusa da compilazione selezionare , 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.

  1. 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>
    
  2. 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:

  1. 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.

  2. 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.

  3. Tutti i file natvis installati e registrati tramite un pacchetto VSIX.

  1. Directory Natvis specifica dell'utente, ad esempio %U edizione Standard RPROFILE%\Documents\Visual Studio 2022\Visualizers.
  1. Directory Natvis specifica dell'utente, ad esempio %U edizione Standard RPROFILE%\Documents\Visual Studio 2019\Visualizers.
  1. 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 SizeArrayItems.

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 \&gt;\&gt; 24),x\</Item\>
\<Item Name="HiByteStatus" Condition="(_flags \&amp; 0xFF000000) == 0"\>"None"\</Item\>
\<Item Name="HiByteStatus" Condition="(_flags \&amp; 0xFF000000) != 0"\>"Some"\</Item\>

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&lt;*&gt;">
    <DisplayString>{{ size={_Mylast - _Myfirst} }}</DisplayString>
    <Expand>
        <Item Name="[size]" ExcludeView="simple">_Mylast - _Myfirst</Item>
        <Item Name="[capacity]" ExcludeView="simple">_Myend - _Myfirst</Item>
        <ArrayItems>
            <Size>_Mylast - _Myfirst</Size>
            <ValuePointer>_Myfirst</ValuePointer>
        </ArrayItems>
    </Expand>
</Type>

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):

Finestra Espressioni di controllo con visualizzazione semplice

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:

  1. Tipo per il quale deve essere usata la visualizzazione (attributo Name ).

  2. A quale valore di un oggetto di tale tipo deve essere simile (elemento DisplayString ).

  3. 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&lt;*&gt;">
    <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,MediumMediumHigh , 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&lt;*&gt;" Priority="MediumLow">
     <DisplayString>{_Callee}</DisplayString>
    <Expand>
        <ExpandedItem>_Callee</ExpandedItem>
    </Expand>
</Type>

<!-- VC 2015 -->
<Type Name="std::reference_wrapper&lt;*&gt;">
    <DisplayString>{*_Ptr}</DisplayString>
    <Expand>
        <Item Name="[ptr]">_Ptr</Item>
    </Expand>
</Type>

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 trueo 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 falsee viene visualizzato il secondo DisplayString elemento.

<Type Name="std::auto_ptr&lt;*&gt;">
  <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&lt;*&gt;">
    <DisplayString>{{ size={_Mylast - _Myfirst} }}</DisplayString>
    <Expand>
        <Item Name="[size]" ExcludeView="simple">_Mylast - _Myfirst</Item>
        <Item Name="[capacity]" ExcludeView="simple">_Myend - _Myfirst</Item>
        <ArrayItems>
            <Size>_Mylast - _Myfirst</Size>
            <ValuePointer>_Myfirst</ValuePointer>
        </ArrayItems>
    </Expand>
</Type>

È 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:

Usare un elemento DisplayString

Nell'espressione x e y, che sono membri di , sono all'interno DisplayString di CPointparentesi 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&lt;wchar_t,*&gt;">
  <DisplayString>{m_pszData,su}</DisplayString>
</Type>

L'oggetto CStringT viene visualizzato in una finestra di variabile simile all'esempio seguente:

Elemento DisplayStringT CString

L'aggiunta di un StringView elemento indica al debugger che può visualizzare il valore come visualizzazione di testo.

<Type Name="ATL::CStringT&lt;wchar_t,*&gt;">
  <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.

Dati CStringT con il visualizzatore StringView

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, leftright, 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:

CRect con espansione dell'elemento Item

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&lt;*&gt;">
  <DisplayString>{{size = {_Mylast - _Myfirst}}}</DisplayString>
  <Expand>
    <Item Name="[size]">_Mylast - _Myfirst</Item>
    <Item Name="[capacity]">(_Myend - _Myfirst)</Item>
    <ArrayItems>
      <Size>_Mylast - _Myfirst</Size>
      <ValuePointer>_Myfirst</ValuePointer>
    </ArrayItems>
  </Expand>
</Type>

Un elemento std::vector visualizza i singoli elementi quando viene espanso nella finestra delle variabili:

std::vector con l'espansione ArrayItems

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 non void*è ).

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&lt;*,*&gt;">
  <DisplayString>extent = {_M_extent}</DisplayString>
  <Expand>
    <Item Name="extent">_M_extent</Item>
    <ArrayItems Condition="_M_buffer_descriptor._M_data_ptr != 0">
      <Direction>Forward</Direction>
      <Rank>$T2</Rank>
      <Size>_M_extent._M_base[$i]</Size>
      <ValuePointer>($T1*) _M_buffer_descriptor._M_data_ptr</ValuePointer>
      <LowerBound>0</LowerBound>
    </ArrayItems>
  </Expand>
</Type>
  • Direction 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.
  • 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],...

Ecco l'aspetto di un oggetto bidimensionale Concurrency::array nella finestra del debugger:

Matrice bidimensionale con espansione ArrayItems

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&lt;*&gt;">
  <DisplayString>{{size = {_M_vector._M_index}}}</DisplayString>
  <Expand>
    <Item Name="[size]">_M_vector._M_index</Item>
    <IndexListItems>
      <Size>_M_vector._M_index</Size>
      <ValueNode>*(_M_vector._M_array[$i])</ValueNode>
    </IndexListItems>
  </Expand>
</Type>

L'unica differenza tra ArrayItems e è , ValueNodeche prevede l'espressione completa per l'elemento i con il parametro implicito $iIndexListItems.

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&lt;*,*&gt;">
  <DisplayString>{{Count = {m_nElements}}}</DisplayString>
  <Expand>
    <Item Name="Count">m_nElements</Item>
    <LinkedListItems>
      <Size>m_nElements</Size>
      <HeadPointer>m_pHead</HeadPointer>
      <NextPointer>m_pNext</NextPointer>
      <ValueNode>m_element</ValueNode>
    </LinkedListItems>
  </Expand>
</Type>

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, IndexListItemso 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&lt;*,*,*,*&gt;">
    <AlternativeType Name="ATL::CMapToInterface&lt;*,*,*&gt;"/>
    <AlternativeType Name="ATL::CMapToAutoPtr&lt;*,*,*&gt;"/>
    <DisplayString>{{Count = {m_nElements}}}</DisplayString>
    <Expand>
      <CustomListItems MaxItemsPerView="5000" ExcludeView="Test">
        <Variable Name="iBucket" InitialValue="-1" />
        <Variable Name="pBucket" InitialValue="m_ppBins == nullptr ? nullptr : *m_ppBins" />
        <Variable Name="iBucketIncrement" InitialValue="-1" />

        <Size>m_nElements</Size>
        <Exec>pBucket = nullptr</Exec>
        <Loop>
          <If Condition="pBucket == nullptr">
            <Exec>iBucket++</Exec>
            <Exec>iBucketIncrement = __findnonnull(m_ppBins + iBucket, m_nBins - iBucket)</Exec>
            <Break Condition="iBucketIncrement == -1" />
            <Exec>iBucket += iBucketIncrement</Exec>
            <Exec>pBucket = m_ppBins[iBucket]</Exec>
          </If>
          <Item>pBucket,na</Item>
          <Exec>pBucket = pBucket->m_pNext</Exec>
        </Loop>
      </CustomListItems>
    </Expand>
</Type>

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&lt;*&gt;">
  <DisplayString>{{size = {_Mysize}}}</DisplayString>
  <Expand>
    <Item Name="[size]">_Mysize</Item>
    <Item Name="[comp]">comp</Item>
    <TreeItems>
      <Size>_Mysize</Size>
      <HeadPointer>_Myhead->_Parent</HeadPointer>
      <LeftPointer>_Left</LeftPointer>
      <RightPointer>_Right</RightPointer>
      <ValueNode Condition="!((bool)_Isnil)">_Myval</ValueNode>
    </TreeItems>
  </Expand>
</Type>

La sintassi è simile al LinkedListItems nodo. LeftPointer, RightPointere 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:

<espansione predefinita di auto_ptr vettore<>>

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&lt;*&gt;">
  <DisplayString>auto_ptr {*_Myptr}</DisplayString>
  <Expand>
    <ExpandedItem>_Myptr</ExpandedItem>
  </Expand>
</Type>

<espansione di auto_ptr vettore<int>> 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 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&lt;*,*&gt;">
  <DisplayString>extent = {_M_extent}</DisplayString>
  <Expand>
    <Item Name="extent" Condition="_M_buffer_descriptor._M_data_ptr == 0">_M_extent</Item>
    <ArrayItems Condition="_M_buffer_descriptor._M_data_ptr != 0">
      <Rank>$T2</Rank>
      <Size>_M_extent._M_base[$i]</Size>
      <ValuePointer>($T1*) _M_buffer_descriptor._M_data_ptr</ValuePointer>
    </ArrayItems>
    <Synthetic Name="Array" Condition="_M_buffer_descriptor._M_data_ptr == 0">
      <DisplayString>Array members can be viewed only under the GPU debugger</DisplayString>
    </Synthetic>
  </Expand>
</Type>

Concurrency::Array con espansione degli elementi sintetici

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&lt;*&gt;">
  <Intrinsic Name="size" Expression="(size_t)(_Mypair._Myval2._Mylast - _Mypair._Myval2._Myfirst)" />
  <Intrinsic Name="capacity" Expression="(size_t)(_Mypair._Myval2._Myend - _Mypair._Myval2._Myfirst)" />
  <DisplayString>{{ size={size()} }}</DisplayString>
  <Expand>
    <Item Name="[capacity]" ExcludeView="simple">capacity()</Item>
    <Item Name="[allocator]" ExcludeView="simple">_Mypair</Item>
    <ArrayItems>
      <Size>size()</Size>
      <ValuePointer>_Mypair._Myval2._Myfirst</ValuePointer>
    </ArrayItems>
  </Expand>
</Type>

Elemento HResult

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 oggetto UIVisualizer. 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:

    Menu di scelta rapida del menu UIVisualizer

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&lt;int,*&gt;">
  <UIVisualizer ServiceId="{5452AFEA-3DF6-46BB-9177-C0B08F318025}" Id="1" />
</Type>

È possibile visualizzare un esempio di nell'estensione UIVisualizerImage 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 Conditionattributi , IncludeViewe 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.