Erstellen benutzerdefinierter Ansichten von C++-Objekten im Debugger mithilfe des Natvis-Frameworks
Das Visual Studio Natvis Framework passt die Darstellung systemeigener Typen in Debugger-Variablenfenstern an, wie zum Beispiel in den Locals und Watch Fenstern und in DataTips. Natvis-Visualisierungen können dabei helfen, die Typen, die Sie beim Debuggen erstellen, sichtbarer zu machen.
Natvis ersetzt die autoexp.dat-Datei in früheren Versionen von Visual Studio durch XML-Syntax, bessere Diagnosemöglichkeiten, Versionierung und Unterstützung für mehrere Dateien.
Anmerkung
Natvis-Anpassungen funktionieren mit Klassen und Strukturen, aber nicht mit Typedefs.
Natvis-Visualisierungen
Sie verwenden das Natvis-Framework, um Visualisierungsregeln für die von Ihnen erstellten Typen zu erstellen, damit Entwickler sie während des Debuggens einfacher sehen können.
Die folgende Abbildung zeigt z. B. eine Variable vom Typ Windows::UI::XAML::Controls::TextBox in einem Debuggerfenster ohne angewendete benutzerdefinierte Visualisierungen.
In der hervorgehobenen Zeile wird die Text
-Eigenschaft der TextBox
-Klasse angezeigt. Die komplexe Klassenhierarchie erschwert das Auffinden dieser Eigenschaft. Der Debugger weiß nicht, wie der benutzerdefinierte Zeichenfolgentyp interpretiert wird, sodass die Zeichenfolge nicht im Textfeld angezeigt wird.
Dieselbe TextBox
sieht im Variablenfenster viel einfacher aus, wenn benutzerdefinierte Natvis-Visualisierungsregeln angewendet werden. Die wichtigen Member der Klasse werden zusammen angezeigt, und der Debugger zeigt den zugrunde liegenden Zeichenfolgenwert des benutzerdefinierten Zeichenfolgentyps an.
Verwenden von NATVIS-Dateien in C++-Projekten
Natvis verwendet NATVIS--Dateien, um Visualisierungsregeln anzugeben. Eine .natvis--Datei ist eine XML-Datei mit der Erweiterung .natvis. Das Natvis-Schema wird in <VS-Installationsordner>\Xml\Schemas\1033\natvis.xsddefiniert.
Die grundlegende Struktur einer NATVIS--Datei ist ein oder mehrere Type
Elemente, die Visualisierungseinträge darstellen. Der vollqualifizierte Name jedes Type
Elements wird in seinem Name
-Attribut angegeben.
<?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 stellt einige .natvis- Dateien im <VS-Installationsordner>\Common7\Packages\Debugger\Visualizers Ordner bereit. Diese Dateien verfügen über Visualisierungsregeln für viele gängige Typen und können als Beispiele für das Schreiben von Visualisierungen für neue Typen dienen.
Hinzufügen einer NATVIS-Datei zu einem C++-Projekt
Sie können einem beliebigen C++-Projekt eine .natvis--Datei hinzufügen.
So fügen Sie eine neue NATVIS-Datei hinzu
Wählen Sie den C++-Projektknoten im Projektmappen-Exploreraus, und wählen Sie Projekt>Neues Element hinzufügenaus, oder klicken Sie mit der rechten Maustaste auf das Projekt, und wählen Sie Hinzufügen>Neues Elementaus.
Wenn nicht alle Elementvorlagen angezeigt werden, wählen Sie Alle Vorlagen anzeigenaus.
Wählen Sie im Dialogfeld Neues Element hinzufügen die Option Visual C++>Dienstprogramm>Debugger-Visualisierungsdatei (.natvis) aus.
Benennen Sie die Datei, und wählen Sie Hinzufügen aus.
Die neue Datei wird dem Projektmappen-Explorerhinzugefügt und im Dokumentbereich von Visual Studio geöffnet.
Der Visual Studio-Debugger lädt NATVIS- Dateien in C++-Projekten automatisch und enthält sie standardmäßig auch in der PDB--Datei, wenn das Projekt erstellt wird. Wenn Sie die integrierte App debuggen, lädt der Debugger die NATVIS- Datei aus der PDB--Datei, auch wenn das Projekt nicht geöffnet ist. Wenn Sie nicht möchten, dass die NATVIS--Datei in der .pdb-enthalten ist, können Sie sie aus der integrierten PDB--Datei ausschließen.
Um eine .natvis--Datei von einer .pdb-auszuschließen:
Wählen Sie die .natvis Datei im Solution Exploreraus, und wählen Sie das Eigenschaften-Symbol aus, oder klicken Sie mit der rechten Maustaste auf die Datei und wählen Sie Eigenschaftenaus.
Verwenden Sie den Dropdownpfeil neben Vom Build ausgeschlossen, und wählen Sie dann Ja und anschließend OK aus.
Anmerkung
Verwenden Sie zum Debuggen ausführbarer Projekte die Lösungselemente, um alle NATVIS- Dateien hinzuzufügen, die sich nicht im .pdb-befinden, da kein C++-Projekt verfügbar ist.
Anmerkung
Natvis-Regeln, die aus einer PDB- geladen wurden, gelten nur für die Typen in den Modulen, auf die sich die .pdb bezieht. Wenn z. B. Module1.pdb einen Natvis-Eintrag für einen Typ namens Test
hat, gilt er nur für die Test
-Klasse in Module1.dll. Wenn ein anderes Modul auch eine Klasse mit dem Namen Test
definiert, gilt der Module1.pdb Natvis-Eintrag nicht.
So installieren und registrieren Sie eine NATVIS--Datei über ein VSIX-Paket:
Ein VSIX-Paket kann NATVIS--Dateien installieren und registrieren. Unabhängig davon, wo sie installiert sind, werden alle registrierten NATVIS- Dateien beim Debuggen automatisch aufgenommen.
Schließen Sie die NATVIS--Datei in das VSIX-Paket ein. Zum Beispiel, für die folgende Projektdatei:
<?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>
Registrieren Sie die .natvis--Datei in der source.extension.vsixmanifest--Datei.
<?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>
Natvis-Dateispeicherorte
Sie können NATVIS- Dateien zu Ihrem Benutzerverzeichnis oder einem Systemverzeichnis hinzufügen, wenn sie auf mehrere Projekte angewendet werden sollen.
Die NATVIS--Dateien werden in der folgenden Reihenfolge ausgewertet:
Jede NATVIS-Datei, die in eine PDB-Datei eingebettet ist, die Sie debuggen, sofern keine Datei mit demselben Namen in dem geladenen Projekt vorhanden ist.
Alle .natvis--Dateien, die sich in einem geladenen C++-Projekt oder einer Lösung auf oberster Ebene befinden. Diese Gruppe enthält alle geladenen C++-Projekte, einschließlich Klassenbibliotheken, aber keine Projekte in anderen Sprachen.
Alle .natvis Dateien, die über ein VSIX-Paket installiert und registriert wurden.
- Das benutzerspezifische Natvis-Verzeichnis (z. B. %USERPROFILE%\Documents\Visual Studio 2022\Visualizers).
- Das benutzerspezifische Natvis-Verzeichnis (z. B. %USERPROFILE%\Documents\Visual Studio 2019\Visualizers).
- Das systemweite Natvis-Verzeichnis (<Microsoft Visual Studio-Installationsordner>\Common7\Packages\Debugger\Visualizers). Dieses Verzeichnis verfügt über die NATVIS- Dateien, die mit Visual Studio installiert sind. Wenn Sie über Administratorberechtigungen verfügen, können Sie diesem Verzeichnis Dateien hinzufügen.
Ändern von NATVIS-Dateien beim Debuggen
Sie können eine NATVIS--Datei in der IDE beim Debuggen des Projekts ändern. Öffnen Sie die Datei in derselben Instanz von Visual Studio, mit der Sie debuggen, ändern sie, und speichern Sie sie. Sobald die Datei gespeichert wird, aktualisieren sich die Watch- und Locals-Fenster, um die Änderung widerzuspiegeln.
Sie können auch .natvis--Dateien in einer Lösung, die Sie debuggen, hinzufügen oder löschen, und Visual Studio fügt die relevanten Visualisierungen hinzu oder entfernt sie.
Sie können .natvis Dateien, die während des Debuggens in PDB- dateien eingebettet sind, nicht aktualisieren.
Wenn Sie die NATVIS- Datei außerhalb von Visual Studio ändern, werden die Änderungen nicht automatisch wirksam. Zum Aktualisieren der Debuggerfenster können Sie den Befehl .natvisreload im Fenster Direkt neu auswerten. Dann werden die Änderungen wirksam, ohne die Debugsitzung neu zu starten.
Verwenden Sie außerdem den Befehl .natvisreload, um die NATVIS--Datei auf eine neuere Version zu aktualisieren. Die NATVIS-Datei kann z. B. in die Quellcodeverwaltung eingecheckt werden, und Sie möchten die letzten Änderungen übernehmen, die jemand anderes vorgenommen hat.
Ausdrücke und Formatierungen
Natvis-Visualisierungen verwenden C++-Ausdrücke, um die anzuzeigenden Datenelemente anzugeben. Beachten Sie neben den Verbesserungen und Einschränkungen von C++-Ausdrücken im Debugger, die in Kontextoperator (C++)beschrieben werden, Folgendes:
Natvis-Ausdrücke werden im Kontext des zu visualisierenden Objekts und nicht im aktuellen Stapelframe ausgewertet. Beispielsweise bezieht sich
x
in einem Natvis-Ausdruck auf das Feld mit dem Namen x in dem objekt, das visualisiert wird, nicht auf eine lokale Variable mit dem Namen x in der aktuellen Funktion. Sie können nicht auf lokale Variablen in Natvis-Ausdrücken zugreifen, obwohl Sie auf globale Variablen zugreifen können.Natvis-Ausdrücke erlauben keine Funktionsevaluationen oder Seiteneffekte. Funktionsaufrufe und Zuweisungsoperatoren werden ignoriert. Da intrinsische Debuggerfunktionen keine Nebeneffekte haben, können sie beliebig von jedem Natvis-Ausdruck aufgerufen werden, obwohl andere Funktionsaufrufe nicht zulässig sind.
Sie können die Anzeige eines Ausdrucks steuern, indem Sie einen der unter Formatbezeichner in C++ beschriebenen Formatbezeichner verwenden. Format-Spezifizierer werden ignoriert, wenn der Eintrag intern von Natvis verwendet wird, z. B. der
Size
-Ausdruck in einer ArrayItems-Expansion.
Anmerkung
Da es sich bei dem Natvis-Dokument um ein XML-Dokument handelt, können Sie in Ihren Ausdrücken die Operatoren kaufmännisches Und, Größer-als-Zeichen, Kleiner-als-Zeichen und Schiebeoperatoren nicht direkt verwenden. Sie müssen für diese Zeichen sowohl im Elementtext als auch in den Bedingungsanweisungen Escapezeichen verwenden. Zum Beispiel:
\<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\>
Natvis-Ansichten
Sie können verschiedene Natvis-Ansichten definieren, um Typen auf unterschiedliche Weise anzuzeigen. Hier ist beispielsweise eine Visualisierung von std::vector
, die eine vereinfachte Ansicht namens simple
definiert. Die DisplayString
und die ArrayItems
Elemente werden in der Standardansicht und in der simple
Ansicht angezeigt, während die [size]
und [capacity]
Elemente nicht in der simple
Ansicht angezeigt werden.
<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>
Verwenden Sie im Fenster Watch den ,View Formatbezeichner, um eine alternative Ansicht anzugeben. Die einfache Ansicht wird als vec,view(simple)angezeigt:
Natvis-Fehler
Wenn beim Debugger Fehler in einem Visualisierungseintrag auftreten, werden diese ignoriert. Er zeigt entweder den Typ in seinem unformatierten Formular an oder wählt eine andere geeignete Visualisierung aus. Sie können die Natvis-Diagnose verwenden, um zu verstehen, warum der Debugger einen Visualisierungseintrag ignoriert hat, und um zugrunde liegende Syntax- und Analysefehler anzuzeigen.
So aktivieren Sie die Natvis-Diagnose:
- Legen Sie unter Extras>Optionen (oder Debuggen>Optionen) >Debuggen>Ausgabefenster die Natvis-Diagnosemeldungen (nur C++) auf Fehler, Warnung oder Ausführlich fest, und wählen Sie dann OK aus.
Die Fehler werden im Fenster Ausgabe angezeigt.
Natvis-Syntaxreferenz
Die folgenden Elemente und Attribute können in der Natvis-Datei verwendet werden.
AutoVisualizer-Element
Das AutoVisualizer
-Element ist der Stammknoten der NATVIS--Datei und enthält das Namespace-xmlns:
-Attribut.
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
.
.
</AutoVisualizer>
Das AutoVisualizer
-Element kann folgende untergeordnete Elemente aufweisen: Typ, HResult, UIVisualizer und CustomVisualizer.
Typenelement
Ein einfaches Type
sieht wie in diesem Beispiel aus:
<Type Name="[fully qualified type name]">
<DisplayString Condition="[Boolean expression]">[Display value]</DisplayString>
<Expand>
...
</Expand>
</Type>
Das Type
-Element gibt Folgendes an:
Für welchen Typ die Visualisierung verwendet werden soll (das attribut
Name
).Wie der Wert eines Objekts dieses Typs aussehen soll (das
DisplayString
-Element).Wie die Elemente des Typs aussehen sollen, wenn der Benutzer den Typ in einem Variablenfenster (dem Knoten
Expand
) erweitert.
Auf Vorlagen basierende Klassen
Das Name
Attribut des Type
-Elements akzeptiert ein Sternchen *
als Wildcardzeichen, das für Vorlagenklassennamen verwendet werden kann.
Im folgenden Beispiel wird dieselbe Visualisierung verwendet, ob es sich bei dem Objekt um eine CAtlArray<int>
oder eine CAtlArray<float>
handelt. Wenn ein bestimmter Visualisierungseintrag für eine CAtlArray<float>
vorhanden ist, hat sie Vorrang vor dem generischen.
<Type Name="ATL::CAtlArray<*>">
<DisplayString>{{Count = {m_nSize}}}</DisplayString>
</Type>
Sie können auf Vorlagenparameter im Visualisierungseintrag verweisen, indem Sie Makros $T 1, $T 2 usw. verwenden. Beispiele für diese Makros finden Sie in den NATVIS- Dateien, die mit Visual Studio ausgeliefert wurden.
Typenabgleich in der Schnellansicht
Wenn ein Visualisierungseintrag nicht überprüft werden kann, wird die nächste verfügbare Visualisierung verwendet.
Vererbbares Attribut
Das optionale attribut Inheritable
gibt an, ob eine Visualisierung nur für einen Basistyp oder für einen Basistyp und alle abgeleiteten Typen gilt. Der Standardwert von Inheritable
ist true
.
Im folgenden Beispiel gilt die Visualisierung nur für den BaseClass
Typ:
<Type Name="Namespace::BaseClass" Inheritable="false">
<DisplayString>{{Count = {m_nSize}}}</DisplayString>
</Type>
Priority-Attribut
Das optionale attribut Priority
gibt die Reihenfolge an, in der alternative Definitionen verwendet werden sollen, wenn eine Definition nicht analysiert werden kann. Mögliche Werte von Priority
sind: Low
, MediumLow
,Medium
, MediumHigh
und High
. Der Standardwert ist Medium
. Das attribut Priority
unterscheidet nur zwischen Prioritäten innerhalb derselben NATVIS Datei.
Im folgenden Beispiel wird zunächst der Eintrag analysiert, der mit dem STL 2015 übereinstimmt. Wenn dies nicht analysiert werden kann, wird der alternative Eintrag für die Version 2013 der STL verwendet:
<!-- 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>
Optional-Attribut
Sie können ein Optional
-Attribut auf einem beliebigen Knoten platzieren. Wenn ein Unterausdruck innerhalb eines optionalen Knotens nicht analysiert werden kann, ignoriert der Debugger diesen Knoten, wendet jedoch die restlichen Type
Regeln an. Im folgenden Typ ist [State]
nicht optional, [Exception]
ist jedoch optional. Wenn MyNamespace::MyClass
ein Feld mit dem Namen _M_exceptionHolder
hat, werden sowohl der [State]
Knoten als auch der [Exception]
Knoten angezeigt, aber wenn kein _M_exceptionHolder
Feld vorhanden ist, wird nur der [State]
Knoten angezeigt.
<Type Name="MyNamespace::MyClass">
<Expand>
<Item Name="[State]">_M_State</Item>
<Item Name="[Exception]" Optional="true">_M_exceptionHolder</Item>
</Expand>
</Type>
Bedingungsattribut
Das optionale Condition
-Attribut ist für viele Visualisierungselemente verfügbar und gibt an, wann eine Visualisierungsregel verwendet werden soll. Wenn der Ausdruck innerhalb des Bedingungsattributs in false
aufgelöst wird, wird die Visualisierungsregel nicht angewendet. Wenn sie als true
ausgewertet wird oder kein Condition
Attribut vorhanden ist, wird die Visualisierung angewendet. Sie können dieses Attribut für if-else-Logik in den Visualisierungseinträgen verwenden.
Die folgende Visualisierung enthält beispielsweise zwei DisplayString
Elemente für einen intelligenten Zeigertyp. Wenn das _Myptr
-Element leer ist, wird die Bedingung des ersten DisplayString
Elements in true
aufgelöst, sodass das Formular angezeigt wird. Wenn das _Myptr
-Element nicht leer ist, wird die Bedingung als false
ausgewertet, und das zweite DisplayString
-Element wird angezeigt.
<Type Name="std::auto_ptr<*>">
<DisplayString Condition="_Myptr == 0">empty</DisplayString>
<DisplayString>auto_ptr {*_Myptr}</DisplayString>
<Expand>
<ExpandedItem>_Myptr</ExpandedItem>
</Expand>
</Type>
IncludeView- und ExcludeView-Attribute
Die Attribute IncludeView
und ExcludeView
geben Elemente an, die in bestimmten Ansichten angezeigt oder nicht angezeigt werden sollen. In der folgenden Natvis-Spezifikation von std::vector
zeigt die simple
-Ansicht beispielsweise nicht die [size]
und [capacity]
Elemente an.
<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>
Sie können die Attribute IncludeView
und ExcludeView
für Typen und einzelne Member verwenden.
Versionselement
Das Version
-Element bezieht sich auf einen Visualisierungseintrag auf ein bestimmtes Modul und eine bestimmte Version. Das Version
-Element trägt dazu bei, Namenskonflikte zu vermeiden, versehentliche Konflikte zu reduzieren und unterschiedliche Visualisierungen für unterschiedliche Typversionen zu ermöglichen.
Wenn eine allgemeine Headerdatei, die von verschiedenen Modulen verwendet wird, einen Typ definiert, wird die versionsgeschützte Visualisierung nur angezeigt, wenn sich der Typ in der angegebenen Modulversion befindet.
Im folgenden Beispiel gilt die Visualisierung nur für den DirectUI::Border
Typ, der in der Windows.UI.Xaml.dll
von Version 1.0 bis 1.5 gefunden wurde.
<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>
Sie benötigen nicht sowohl Min
als auch Max
. Sie sind optionale Attribute. Platzhalterzeichen werden nicht unterstützt.
Das attribut Name
befindet sich im Format filename.ext, z. B. hello.exe oder some.dll. Es sind keine Pfadnamen zulässig.
DisplayString-Element
Das DisplayString
-Element gibt eine Zeichenfolge an, die als Wert einer Variablen angezeigt werden soll. Sie akzeptiert beliebige Zeichenfolgen, die mit Ausdrücken gemischt sind. Sämtliche Inhalte innerhalb von geschweiften Klammern werden als ein Ausdruck interpretiert. Ein Beispiel ist der folgende DisplayString
-Eintrag:
<Type Name="CPoint">
<DisplayString>{{x={x} y={y}}}</DisplayString>
</Type>
Bedeutet, dass Variablen vom Typ CPoint
wie in dieser Abbildung angezeigt werden:
Im DisplayString
-Ausdruck befinden sich x
und y
, die Member von CPoint
sind, in geschweiften Klammern, sodass ihre Werte ausgewertet werden. Das Beispiel zeigt auch, wie eine geschweifte Klammer mit doppelten geschweiften Klammern ({{
oder }}
) mit Escapezeichen versehen werden kann.
Anmerkung
Das DisplayString
-Element ist das einzige Element, das beliebige Zeichenfolgen und geschweifte Klammernsyntax akzeptiert. Alle anderen Visualisierungselemente akzeptieren nur Ausdrücke, die der Debugger auswerten kann.
StringView-Element
Das StringView
-Element definiert einen Wert, den der Debugger an die integrierte Textschnellansicht senden kann. Angenommen, es gibt z. B. die folgende Visualisierung für den ATL::CStringT
-Typ:
<Type Name="ATL::CStringT<wchar_t,*>">
<DisplayString>{m_pszData,su}</DisplayString>
</Type>
Das CStringT
-Objekt wird in einem Variablenfenster wie in diesem Beispiel angezeigt:
Durch Das Hinzufügen eines StringView
Elements wird dem Debugger mitgeteilt, dass er den Wert als Textvisualisierung anzeigen kann.
<Type Name="ATL::CStringT<wchar_t,*>">
<DisplayString>{m_pszData,su}</DisplayString>
<StringView>m_pszData,su</StringView>
</Type>
Während des Debuggens können Sie das Lupensymbol neben der Variablen auswählen und dann den Text-Visualizer auswählen, um die Zeichenfolge anzuzeigen, auf die m_pszData verweist.
Der Ausdruck {m_pszData,su}
enthält einen C++-Formatbezeichner su, um den Wert als Unicode-Zeichenfolge anzuzeigen. Weitere Informationen finden Sie unter Formatbezeichner in C++.
Element erweitern
Der optionale Knoten Expand
passt die untergeordneten Elemente eines visualisierten Typs an, wenn Sie den Typ in einem Variablenfenster erweitern. Der Knoten Expand
akzeptiert eine Liste untergeordneter Knoten, die die untergeordneten Elemente definieren.
Wenn ein
Expand
-Knoten nicht in einem Visualisierungseintrag angegeben ist, verwenden die untergeordneten Knoten die standardmäßigen Erweiterungsregeln.Wenn ein
Expand
-Knoten ohne untergeordnete Knoten angegeben wird, kann der Typ in den Debugger-Fenstern nicht erweitert werden.
Item-Erweiterung
Das Item
-Element ist das einfachste und am häufigsten verwendete Element in einem Expand
Knoten. DasItem
-Element definiert ein einzelnes untergeordnetes Element. Eine CRect
Klasse mit Feldern top
, left
, right
und bottom
weist beispielsweise den folgenden Visualisierungseintrag auf:
<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>
Im Debuggerfenster sieht der CRect
Typ wie im folgenden Beispiel aus:
Der Debugger wertet die in den elementen Width
und Height
angegebenen Ausdrücke aus und zeigt die Werte in der Spalte Wert des Variablenfensters an.
Der Debugger erstellt automatisch den [Raw View] Knoten für jede benutzerdefinierte Erweiterung. Im vorherigen Screenshot wird der [Raw View]-Knoten erweitert, um zu zeigen, wie sich die Standard-Rohansicht des Objekts von der Natvis-Visualisierung unterscheidet. Mit der Standarderweiterung werden eine Teilstruktur für die Basisklasse erstellt und alle Datenmember der Basisklasse als untergeordnete Elemente aufgeführt.
Anmerkung
Wenn der Ausdruck des Item-Elements auf einen komplexen Typ weist, ist der Item-Knoten selbst erweiterbar.
ArrayItems-Erweiterung
Verwenden Sie den ArrayItems
Knoten, damit der Visual Studio-Debugger den Typ als Array interpretiert und seine einzelnen Elemente anzeigt. Die Visualisierung für std::vector
ist ein gutes Beispiel:
<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>
Im std::vector
-Knoten werden die einzelnen Elemente angezeigt, wenn sie im Variablenfenster erweitert werden:
Der Knoten ArrayItems
muss haben:
- Ein
Size
Ausdruck (der als ganze Zahl ausgewertet werden muss) für den Debugger, um die Länge des Arrays zu verstehen. - Ein
ValuePointer
Ausdruck, der auf das erste Element verweist (das muss ein Zeiger eines Elementtyps sein, der nichtvoid*
ist).
Der Standardwert der unteren Grenze des Arrays ist 0. Verwenden Sie zum Überschreiben des Werts ein LowerBound
-Element. Die NATVIS- dateien, die mit Visual Studio ausgeliefert wurden, enthalten Beispiele.
Anmerkung
Sie können den []
-Operator, z. B. vector[i]
, mit jeder eindimensionalen Arrayvisualisierung verwenden, die ArrayItems
verwendet, auch wenn der Typ selbst (z. B. CATLArray
) diesen Operator nicht zulässt.
Sie können auch mehrdimensionale Arrays angeben. In diesem Fall benötigt der Debugger etwas mehr Informationen, um untergeordnete Elemente ordnungsgemäß anzuzeigen:
<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
gibt an, ob das Array in zeilengerichteter oder spaltengerichteter Reihenfolge angegeben ist.Rank
gibt den Rang des Arrays an.- Das
Size
-Element akzeptiert den impliziten$i
-Parameter, der durch den Dimensionsindex ersetzt wird, um die Länge des Arrays in dieser Dimension zu ermitteln.- Im vorherigen Beispiel sollte der Ausdruck
_M_extent.M_base[0]
die Länge der 0. Dimension angeben,_M_extent._M_base[1]
das erste usw.
- Im vorherigen Beispiel sollte der Ausdruck
- Die
LowerBound
gibt die untere Grenze jeder Dimension des Arrays an. Für mehrdimensionale Arrays können Sie einen Ausdruck angeben, der den impliziten$i
-Parameter verwendet. Der parameter$i
wird durch den Dimensionsindex ersetzt, um die untere Grenze des Arrays in dieser Dimension zu finden.- Im vorherigen Beispiel beginnen alle Dimensionen bei 0. Wenn Sie jedoch
($i == 1) ? 1000 : 100
als untere Grenze hatten, beginnt die 0. Dimension bei 100, und die erste Dimension beginnt bei 1000.- , z. B.
[100, 1000], [100, 1001], [100, 1002], ... [101, 1000], [101, 1001],...
- , z. B.
- Im vorherigen Beispiel beginnen alle Dimensionen bei 0. Wenn Sie jedoch
So sieht ein zweidimensionales Concurrency::array
-Objekt im Debuggerfenster aus:
IndexListItems-Erweiterung
Sie können ArrayItems
Erweiterung nur verwenden, wenn die Arrayelemente zusammenhängend im Speicher angeordnet sind. Der Debugger wechselt zum nächsten Element, indem er einfach seinen Zeiger erhöht. Wenn Sie den Index zum Wertknoten manipulieren müssen, verwenden Sie IndexListItems
-Knoten. Hier ist eine Visualisierung mit einem IndexListItems
-Knoten:
<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>
Der einzige Unterschied zwischen ArrayItems
und IndexListItems
ist ValueNode
, das den vollständigen Ausdruck für das ite-Element mit dem impliziten $i
-Parameter erwartet.
Anmerkung
Sie können den []
-Operator, z. B. vector[i]
, mit jeder eindimensionalen Arrayvisualisierung verwenden, die IndexListItems
verwendet, auch wenn der Typ selbst (z. B. CATLArray
) diesen Operator nicht zulässt.
LinkedListItems-Erweiterung
Wenn der Schnellansichtstyp eine verknüpfte Liste darstellt, kann der Debugger die untergeordneten Elemente mithilfe eines LinkedListItems
-Knotens anzeigen. Die folgende Visualisierung für den CAtlList
Typ verwendet 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>
Das Size
-Element bezieht sich auf die Länge der Liste. HeadPointer
verweist auf das erste Element, NextPointer
auf das nächste Element verweist, und ValueNode
auf den Wert des Elements verweist.
Der Debugger wertet die Ausdrücke NextPointer
und ValueNode
im Kontext des Knotens LinkedListItems
aus und nicht im Kontext des übergeordneten Listentyps. Im vorherigen Beispiel weist CAtlList
eine CNode
Klasse auf (in atlcoll.h
gefunden), die ein Knoten der verknüpften Liste ist. m_pNext
und m_element
sind Felder dieser CNode
Klasse, nicht der CAtlList
Klasse.
ValueNode
können leer bleiben, oder this
kann verwendet werden, um auf den Knoten LinkedListItems
selbst zu verweisen.
CustomListItems-Erweiterung
Mit der erweiterung CustomListItems
können Sie benutzerdefinierte Logik zum Durchlaufen einer Datenstruktur wie einer Hashtabelle schreiben. Verwenden Sie CustomListItems
, um Datenstrukturen zu visualisieren, die C++-Ausdrücke für alles verwenden können, was Sie auswerten müssen, aber nicht ganz in die Form für ArrayItems
, IndexListItems
oder LinkedListItems
passen.
Sie können Exec
verwenden, um Code innerhalb einer CustomListItems
Erweiterung auszuführen, indem Sie die variablen und Objekte verwenden, die in der Erweiterung definiert sind. Sie können logische Operatoren, arithmetische Operatoren und Zuordnungsoperatoren mit Exec
verwenden. Sie können Exec
nicht verwenden, um Funktionen auszuwerten, mit Ausnahme von den systeminternen Debugger-Funktionen, die vom C++-Ausdrucksauswerter unterstützt werden.
Die folgende Visualisierung für CAtlMap
ist ein hervorragendes Beispiel, in dem CustomListItems
geeignet ist.
<Type Name="ATL::CAtlMap<*,*,*,*>">
<AlternativeType Name="ATL::CMapToInterface<*,*,*>"/>
<AlternativeType Name="ATL::CMapToAutoPtr<*,*,*>"/>
<DisplayString>{{Count = {m_nElements}}}</DisplayString>
<Expand>
<CustomListItems MaxItemsPerView="5000" ExcludeView="Test">
<Variable Name="iBucket" InitialValue="-1" />
<Variable Name="pBucket" InitialValue="m_ppBins == nullptr ? nullptr : *m_ppBins" />
<Variable Name="iBucketIncrement" InitialValue="-1" />
<Size>m_nElements</Size>
<Exec>pBucket = nullptr</Exec>
<Loop>
<If Condition="pBucket == nullptr">
<Exec>iBucket++</Exec>
<Exec>iBucketIncrement = __findnonnull(m_ppBins + iBucket, m_nBins - iBucket)</Exec>
<Break Condition="iBucketIncrement == -1" />
<Exec>iBucket += iBucketIncrement</Exec>
<Exec>pBucket = m_ppBins[iBucket]</Exec>
</If>
<Item>pBucket,na</Item>
<Exec>pBucket = pBucket->m_pNext</Exec>
</Loop>
</CustomListItems>
</Expand>
</Type>
TreeItems-Erweiterung
Wenn der Schnellansichtstyp eine Struktur darstellt, kann der Debugger die Struktur durchlaufen und seine untergeordneten Elemente mithilfe eines TreeItems
-Knotens anzeigen. Dies ist die Visualisierung für den std::map
Typ mithilfe eines TreeItems
Knotens:
<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>
Die Syntax ähnelt dem Knoten LinkedListItems
. LeftPointer
, RightPointer
und ValueNode
werden im Kontext der Baumknotenklasse ausgewertet. ValueNode
kann leer bleiben, oder Sie können this
verwenden, um auf den TreeItems
-Knoten selbst zu verweisen.
Erweiterung von ExpandedItem
Das ExpandedItem
-Element generiert eine aggregierte untergeordnete Ansicht, indem die Eigenschaften von Basisklassen oder Datenmembern so angezeigt werden, als ob sie untergeordnete Elemente des Schnellansichtstyps wären. Der Debugger wertet den angegebenen Ausdruck aus und fügt die untergeordneten Knoten des Ergebnisses an die untergeordnete Liste des visualisierten Typs an.
Der intelligente Zeigertyp auto_ptr<vector<int>>
wird üblicherweise angezeigt als:
Um die Werte des Vektors anzuzeigen, müssen Sie zwei Ebenen im Variablenfenster aufschlüsseln und das _Myptr
-Element durchlaufen. Indem Sie ein ExpandedItem
-Element hinzufügen, können Sie die _Myptr
Variable aus der Hierarchie entfernen und die Vektorelemente direkt anzeigen:
<Type Name="std::auto_ptr<*>">
<DisplayString>auto_ptr {*_Myptr}</DisplayString>
<Expand>
<ExpandedItem>_Myptr</ExpandedItem>
</Expand>
</Type>
Das folgende Beispiel zeigt, wie Eigenschaften aus der Basisklasse in einer abgeleiteten Klasse aggregiert werden. Angenommen, die CPanel
Klasse wird von CFrameworkElement
abgeleitet. Anstatt die Eigenschaften, die aus der Basisklasse CFrameworkElement
stammen, zu wiederholen, fügt die ExpandedItem
Knotenvisualisierung diese Eigenschaften an die untergeordnete Liste der CPanel
Klasse an.
<Type Name="CPanel">
<DisplayString>{{Name = {*(m_pstrName)}}}</DisplayString>
<Expand>
<Item Name="IsItemsHost">(bool)m_bItemsHost</Item>
<ExpandedItem>*(CFrameworkElement*)this,nd</ExpandedItem>
</Expand>
</Type>
Der nd Formatbezeichner, der den Visualisierungsabgleich für die abgeleitete Klasse deaktiviert, ist hier erforderlich. Andernfalls würde der Ausdruck *(CFrameworkElement*)this
dazu führen, dass die CPanel
Visualisierung erneut angewendet wird, da die Standardregeln für den Visualisierungstypabgleich sie als die geeignetste ansehen. Verwenden Sie den nd Formatbezeichner, um den Debugger anzuweisen, die Basisklassenvisualisierung zu verwenden, oder die Standarderweiterung, wenn die Basisklasse keine Visualisierung aufweist.
Erweiterung synthetischer Artikel
Während das ExpandedItem
-Element eine flachere Ansicht der Daten bereitstellt, indem Hierarchien eliminiert werden, führt der Synthetic
Knoten das Gegenteil aus. Es ermöglicht Ihnen, ein künstliches untergeordnetes Element zu erstellen, das nicht das Ergebnis eines Ausdrucks ist. Das künstliche Element kann eigene untergeordnete Elemente aufweisen. Im folgenden Beispiel verwendet die Visualisierung für den typ Concurrency::array
einen Synthetic
Knoten, um dem Benutzer eine Diagnosemeldung anzuzeigen:
<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>
Inhärente Erweiterung
Eine benutzerdefinierte systeminterne Funktion, die aus einem Ausdruck aufgerufen werden kann. Ein <Intrinsic>
-Element muss von einer Debuggerkomponente begleitet werden, die die Funktion über die IDkmIntrinsicFunctionEvaluator140-Schnittstelle implementiert. Weitere Informationen zum Implementieren einer benutzerdefinierten systeminternen Funktion finden Sie unter Implementieren der benutzerdefinierten systeminternen NatVis-Funktion.
<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>
HResult-Element
Mit dem HResult
-Element können Sie die angezeigten Informationen für ein HRESULT- in Debuggerfenstern anpassen. Das HRValue
-Element muss den 32-Bit-Wert des HRESULT- enthalten, der angepasst werden soll. Das HRDescription
-Element enthält die Informationen, die im Debuggerfenster angezeigt werden sollen.
<HResult Name="MY_E_COLLECTION_NOELEMENTS">
<HRValue>0xABC0123</HRValue>
<HRDescription>No elements in the collection.</HRDescription>
</HResult>
UIVisualizer-Element
Ein UIVisualizer
-Element registriert ein grafisches Visualisierungsplugin beim Debugger. Eine grafische Visualisierung erstellt ein Dialogfeld oder eine andere Schnittstelle, die eine Variable oder ein Objekt auf eine Weise anzeigt, die mit dem Datentyp konsistent ist. Das Visualizer-Plug-In muss als VSPackage-erstellt werden und muss einen Dienst verfügbar machen, den der Debugger nutzen kann. Die NATVIS--Datei enthält Registrierungsinformationen für das Plug-In, z. B. den Namen, den global eindeutigen Bezeichner (GUID) des verfügbar gemachten Diensts und die Typen, die er visualisieren kann.
Hier ist ein Beispiel für ein UIVisualizer-Element:
<?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>
Ein
ServiceId
-Id
Attributpaar identifiziert einUIVisualizer
. DieServiceId
ist die GUID des Diensts, den das Visualizerpaket verfügbar macht.Id
ist ein eindeutiger Bezeichner, der Visualisierungen unterscheidet, wenn ein Dienst mehrere bereitstellt. Im vorherigen Beispiel stellt derselbe Visualizerdienst zwei Visualisierungen bereit.Das attribut
MenuName
definiert einen Visualizernamen, der in der Dropdownliste neben dem Lupensymbol im Debugger angezeigt werden soll. Zum Beispiel:
Jeder Typ, der in der .natvis--Datei definiert ist, muss explizit alle UI-Visualisierungen auflisten, die ihn anzeigen können. Der Debugger vergleicht die Visualisierer-Verweise in den Typeneinträgen mit den registrierten Visualisierern. Der folgende Typeintrag für std::vector
verweist z. B. auf die UIVisualizer
im vorherigen Beispiel.
<Type Name="std::vector<int,*>">
<UIVisualizer ServiceId="{5452AFEA-3DF6-46BB-9177-C0B08F318025}" Id="1" />
</Type>
Sie können ein Beispiel für eine UIVisualizer
in der Image Watch Erweiterung sehen, die zum Anzeigen von In-Memory-Bitmaps dient.
CustomVisualizer-Element
CustomVisualizer
ist ein Erweiterbarkeitspunkt, der eine VSIX-Erweiterung angibt, die Sie schreiben, um Visualisierungen in Visual Studio Code zu steuern. Weitere Informationen zum Schreiben von VSIX-Erweiterungen finden Sie im Visual Studio SDK-.
Es ist viel mehr Arbeit, eine benutzerdefinierte Visualisierung zu schreiben als eine XML-Natvis-Definition, aber Sie sind frei von Einschränkungen darüber, was Natvis tut oder nicht unterstützt. Benutzerdefinierte Visualisierungen haben Zugriff auf den vollständigen Satz von Debuggererweiterungs-APIs, die den Debugprozess abfragen und ändern oder mit anderen Teilen von Visual Studio kommunizieren können.
Sie können die Attribute Condition
, IncludeView
und ExcludeView
für CustomVisualizer
Elemente verwenden.
Begrenzungen
Natvis-Anpassungen funktionieren mit Klassen und Strukturen, aber nicht mit Typedefs.
Natvis unterstützt keine Visualisierungen für Grundtypen (z. B. int
, bool
) oder für Zeiger auf Grundtypen. In diesem Szenario besteht eine Möglichkeit darin, den Formatbezeichner zu verwenden, der für Ihren Anwendungsfall geeignet ist. Wenn Sie z. B. double* mydoublearray
in Ihrem Code verwenden, können Sie im Fenster Watch des Debuggers einen Arrayformatbezeichner verwenden, z. B. den Ausdruck mydoublearray, [100]
, der die ersten 100 Elemente anzeigt.