Partage via


Créer des vues personnalisées d’objets C++ dans le débogueur à l’aide de l’infrastructure Natvis

L’infrastructure Natvis de Visual Studio personnalise la façon dont les types natifs s’affichent dans les fenêtres de variable du débogueur, comme les fenêtres Variables locales et Espion, et dans Info-bulles de données. Les visualisations Natvis peuvent vous aider à rendre les types que vous créez plus visibles pendant le débogage.

Natvis remplace le fichier autoexp.dat dans les versions antérieures de Visual Studio par la syntaxe XML, de meilleurs diagnostics, du contrôle de version et de la prise en charge de plusieurs fichiers.

Note

Les personnalisations Natvis fonctionnent avec des classes et des structs, mais pas avec des typedefs.

Visualisations Natvis

Vous utilisez l’infrastructure Natvis pour créer des règles de visualisation pour les types que vous créez, afin que les développeurs puissent les voir plus facilement pendant le débogage.

Par exemple, l’illustration suivante montre une variable de type Windows ::UI ::XAML ::Controls ::TextBox dans une fenêtre de débogueur sans visualisations personnalisées appliquées.

Visualisation TextBox par défaut

La ligne en surbrillance montre la propriété Text de la classe TextBox. La hiérarchie de classes complexes rend difficile la recherche de cette propriété. Le débogueur ne sait pas comment interpréter le type de chaîne personnalisé. Vous ne pouvez donc pas voir la chaîne contenue dans la zone de texte.

Le même TextBox semble beaucoup plus simple dans la fenêtre de variable quand les règles du visualiseur Natvis personnalisées sont appliquées. Les membres importants de la classe apparaissent ensemble, et le débogueur affiche la valeur de chaîne sous-jacente du type de chaîne personnalisé.

Données TextBox utilisant un visualiseur

Utiliser des fichiers .natvis dans des projets C++

Natvis utilise des fichiers .natvis pour spécifier des règles de visualisation. Un fichier .natvis est un fichier XML avec une extension .natvis. Le schéma Natvis est défini dans <dossier d’installation VS>\Xml\Schemas\1033\natvis.xsd.

La structure de base d’un fichier .natvis est un ou plusieurs éléments Type représentant des entrées de visualisation. Le nom complet de chaque élément Type est spécifié dans son attribut Name.

<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
  <Type Name="MyNamespace::CFoo">
    .
    .
  </Type>

  <Type Name="...">
    .
    .
  </Type>
</AutoVisualizer>

Visual Studio fournit certains fichiers .natvis dans le dossier <Dossier d’installation VS>\Common7\Packages\Debugger\Visualizers. Ces fichiers ont des règles de visualisation pour de nombreux types courants et peuvent servir d’exemples pour écrire des visualisations pour de nouveaux types.

Ajouter un fichier .natvis à un projet C++

Vous pouvez ajouter un fichier .natvis à n’importe quel projet C++.

Pour ajouter un nouveau fichier .natvis :

  1. Sélectionnez le nœud de projet C++ dans Explorateur de solutions, puis sélectionnez Project>Ajouter un nouvel élément, ou cliquez avec le bouton droit sur le projet, puis sélectionnez Ajouter>Nouvel élément.

    Si vous ne voyez pas tous les modèles d’élément, choisissez Afficher tous les modèles.

  2. Dans la boîte de dialogue Ajouter un nouvel élément, sélectionnez Visual C++>Utilitaire>Fichier de visualisation du débogueur (.natvis).

  3. Nommez le fichier, puis sélectionnez Ajouter.

    Le nouveau fichier est ajouté à Explorateur de solutions, puis s’ouvre dans le volet de document Visual Studio.

Le débogueur de Visual Studio charge automatiquement les fichiers .natvis dans les projets C++. Par défaut, il les inclut également dans le fichier .pdb lors de la compilation du projet. Si vous déboguez l’application générée, le débogueur charge le fichier .natvis à partir du fichier .pdb, même si le projet n’est pas ouvert. Si vous ne souhaitez pas que le fichier .natvis soit inclus dans le .pdb, vous pouvez l'exclure du fichier .pdb généré.

Pour exclure un fichier .natvis d’un .pdb:

  1. Sélectionnez le fichier .natvis dans de l’Explorateur de solutions, puis sélectionnez l’icône Propriétés, ou cliquez avec le bouton droit sur le fichier, puis sélectionnez Propriétés.

  2. Cliquez sur la flèche à côté de Exclu du build, sélectionnez Oui, puis cliquez sur OK.

Note

Pour déboguer des projets exécutables, utilisez les éléments de solution pour ajouter des fichiers .natvis qui ne se trouvent pas dans le .pdb, car aucun projet C++ n’est disponible.

Note

Les règles Natvis chargées à partir d’un .pdb s’appliquent uniquement aux types dans les modules auxquels le .pdb fait référence. Par exemple, si module1.pdb a une entrée Natvis pour un type nommé Test, elle s’applique uniquement à la classe Test dans Module1.dll. Si un autre module définit également une classe nommée Test, la Module1.pdb entrée Natvis ne s’applique pas à celle-ci.

Pour installer et inscrire un fichier .natvis via un package VSIX :

Un package VSIX peut installer et inscrire des fichiers .natvis. Quel que soit l’emplacement où ils sont installés, tous les fichiers enregistrés .natvis sont automatiquement récupérés pendant le débogage.

  1. Incluez le fichier .natvis dans le package VSIX. Par exemple, pour le fichier projet suivant :

    <?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. Inscrivez le fichier .natvis dans le fichier 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>
    

Emplacements des fichiers Natvis

Vous pouvez ajouter des fichiers .natvis à votre répertoire utilisateur ou à un répertoire système, si vous souhaitez qu’ils s’appliquent à plusieurs projets.

Les fichiers .natvis sont évalués dans l’ordre suivant :

  1. Les fichiers .natvis incorporés dans un fichier .pdb que vous déboguez, sauf si un fichier du même nom existe dans le projet chargé.

  2. Les fichiers .natvis qui se trouvent dans un projet C++ chargé ou une solution de niveau supérieur. Ce groupe inclut tous les projets C++ chargés, y compris les bibliothèques de classes, mais pas les projets dans d’autres langages.

  3. Les fichiers .natvis installés et inscrits à partir d’un package VSIX.

  1. Répertoire Natvis spécifique à l’utilisateur (par exemple, %USERPROFILE%\Documents\Visual Studio 2022\Visualiseurs).
  1. Répertoire Natvis spécifique à l’utilisateur (par exemple, %USERPROFILE%\Documents\Visual Studio 2019\Visualiseurs).
  1. Répertoire Natvis à l’échelle du système (<dossier d’installation de Microsoft Visual Studio>\Common7\Packages\Debugger\Visualiseurs). Ce répertoire contient les fichiers .natvis installés avec Visual Studio. Si vous disposez d’autorisations d’administrateur, vous pouvez ajouter des fichiers à ce répertoire.

Modifier des fichiers .natvis lors du débogage

Vous pouvez modifier un fichier .natvis dans l’IDE pendant le débogage de son projet. Ouvrez le fichier dans la même instance de Visual Studio que vous déboguez, modifiez-le et enregistrez-le. Dès que le fichier est enregistré, les fenêtres Watch et Locals se mettent à jour pour refléter la modification.

Vous pouvez aussi ajouter ou supprimer des fichiers .natvis dans une solution que vous déboguez, et Visual Studio ajoute ou supprime les visualisations appropriées.

Vous ne pouvez pas mettre à jour les fichiers .natvis incorporés dans des fichiers .pdb pendant le débogage.

Si vous modifiez le fichier .natvis en dehors de Visual Studio, les modifications ne prennent pas effet automatiquement. Pour mettre à jour les fenêtres du débogueur, vous pouvez réévaluer la commande .natvisreload dans la fenêtre Exécution. Ensuite, les modifications prennent effet sans redémarrer la session de débogage.

Utilisez également la commande .natvisreload pour mettre à niveau le fichier .natvis vers une version plus récente. Par exemple, le fichier .natvis peut être archivé dans le contrôle de code source et vous souhaitez récupérer les modifications récentes apportées par quelqu’un d’autre.

Expressions et mise en forme

Les visualisations Natvis utilisent des expressions C++ pour spécifier les éléments de données à afficher. En plus des améliorations et limitations des expressions C++ dans le débogueur qui sont décrites dans opérateur de contexte (C++), veuillez noter les points suivants :

  • Les expressions Natvis sont évaluées dans le contexte de l'objet qui est visualisé, et non dans le frame de pile actuel. Par exemple, x dans une expression Natvis fait référence au champ nommé x dans l’objet en cours de visualisation, et non à une variable locale nommée x dans la fonction actuelle. Vous ne pouvez pas accéder aux variables locales dans les expressions Natvis, même si vous pouvez accéder à des variables globales.

  • Les expressions Natvis n’autorisent pas l’évaluation des fonctions ou les effets secondaires. Les appels de fonction et les opérateurs d’assignation sont ignorés. Comme les fonctions intrinsèques du débogueur n'ont pas d'effets secondaires, elles peuvent librement être appelées à partir de toute expression Natvis, même si d'autres appels de fonction sont interdits.

  • Pour contrôler l’affichage d’une expression, vous pouvez utiliser l’un des spécificateurs de format décrits dans spécificateurs de format en C++. Les spécificateurs de format sont ignorés lorsque l'entrée est utilisée en interne par Natvis, comme l'expression Size dans une expansion ArrayItems .

Remarque

Étant donné que le document Natvis est au format XML, vos expressions ne peuvent pas utiliser directement l'ampersand, l'opérateur supérieur à, l'opérateur inférieur à, ou les opérateurs de décalage. Vous devez échapper ces caractères dans le corps de l’élément et les instructions de condition. Par exemple:
\<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\>

Vues Natvis

Vous pouvez définir différentes vues Natvis pour afficher les types de différentes manières. Par exemple, voici une visualisation de std::vector qui définit une vue simplifiée nommée simple. Les DisplayString et les éléments ArrayItems s’affichent dans l’affichage par défaut et la vue simple, tandis que les éléments [size] et [capacity] ne s’affichent pas dans la vue simple.

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

Dans la fenêtre Espion, utilisez le spécificateur de format ,view pour spécifier une autre vue. La vue simple apparaît sous forme de vec,view(simple):

fenêtre espion Fenêtre Espion avec vue simple

Erreurs Natvis

Lorsque le débogueur rencontre des erreurs dans une entrée de visualisation, il les ignore. Il affiche le type sous sa forme brute ou sélectionne une autre visualisation appropriée. Vous pouvez utiliser les diagnostics Natvis pour comprendre pourquoi le débogueur a ignoré une entrée de visualisation et pour afficher la syntaxe sous-jacente et analyser les erreurs.

Pour activer les diagnostics Natvis :

  • Sous Outils>Options (ou Déboguer>Options) >Débogage>Fenêtre Sortie, définissez Messages de diagnostic Natvis (C++ uniquement) sur Erreur, Avertissement ou Détaillé, puis sélectionnez OK.

Les erreurs s’affichent dans la fenêtre Sortie.

Informations de référence sur la syntaxe Natvis

Les éléments et attributs suivants peuvent être utilisés dans le fichier Natvis.

Élément AutoVisualizer

L'élément AutoVisualizer est le nœud racine du fichier .natvis et il contient l’attribut d’espace de noms xmlns:.

<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
.
.
</AutoVisualizer>

L’élément AutoVisualizer peut avoir des enfants Type, HResult, UIVisualizer et CustomVisualizer.

Type d'élément

Un Type de base ressemble à cet exemple :

<Type Name="[fully qualified type name]">
  <DisplayString Condition="[Boolean expression]">[Display value]</DisplayString>
  <Expand>
    ...
  </Expand>
</Type>

L’élément Type spécifie :

  1. Type de visualisation à utiliser (attribut Name).

  2. À quoi devrait ressembler la valeur d'un objet de ce type (élément DisplayString).

  3. À quoi ressemblent les membres du type lorsque l’utilisateur développe le type dans une fenêtre de variable (nœud Expand).

Classes paramétrées

L’attribut Name de l’élément Type accepte un astérisque * comme caractère générique qui peut être utilisé pour les noms de classes modèles.

Dans l’exemple suivant, la même visualisation est utilisée si l’objet est un CAtlArray<int> ou un CAtlArray<float>. S’il existe une entrée de visualisation spécifique pour un CAtlArray<float>, elle est prioritaire sur celle générique.

<Type Name="ATL::CAtlArray&lt;*&gt;">
    <DisplayString>{{Count = {m_nSize}}}</DisplayString>
</Type>

Vous pouvez référencer des paramètres de modèle dans l’entrée de visualisation à l’aide de macros $T 1, $T 2, etc. Pour trouver des exemples de ces macros, consultez les fichiers .natvis fournis avec Visual Studio.

Correspondance des types de visualiseur

Si une entrée de visualisation ne parvient pas à valider, la visualisation disponible suivante est utilisée.

Attribut héritant

L’attribut facultatif Inheritable spécifie si une visualisation s’applique uniquement à un type de base ou à un type de base et à tous les types dérivés. La valeur par défaut de Inheritable est true.

Dans l’exemple suivant, la visualisation s’applique uniquement au type BaseClass :

<Type Name="Namespace::BaseClass" Inheritable="false">
    <DisplayString>{{Count = {m_nSize}}}</DisplayString>
</Type>

Attribut de priorité

L’attribut facultatif Priority spécifie l’ordre dans lequel utiliser d’autres définitions, si une définition ne parvient pas à analyser. Les valeurs possibles de Priority sont : Low, MediumLow,Medium, MediumHighet High. La valeur par défaut est Medium. L’attribut Priority distingue uniquement les priorités dans le même fichier .natvis.

L’exemple suivant analyse d’abord l’entrée qui correspond au STL 2015. Si l’analyse échoue, l’autre entrée pour la version 2013 de STL est utilisée :

<!-- 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>

Attribut facultatif

Vous pouvez placer un attribut Optional sur n’importe quel nœud. Si une sous-expression à l’intérieur d’un nœud facultatif ne parvient pas à analyser, le débogueur ignore ce nœud, mais applique le reste des règles de Type. Dans le type suivant, [State] n’est pas facultatif, mais [Exception] est facultatif. Si MyNamespace::MyClass a un champ nommé _M_exceptionHolder, le nœud [State] et le nœud [Exception] s’affichent, mais s’il n’y a pas de champ _M_exceptionHolder, seul le nœud [State] apparaît.

<Type Name="MyNamespace::MyClass">
    <Expand>
      <Item Name="[State]">_M_State</Item>
      <Item Name="[Exception]" Optional="true">_M_exceptionHolder</Item>
    </Expand>
</Type>

Attribut Condition

L’attribut facultatif Condition est disponible pour de nombreux éléments de visualisation et spécifie quand utiliser une règle de visualisation. Si l’expression à l’intérieur de l’attribut de condition est résolue en false, la règle de visualisation ne s’applique pas. Si elle prend la valeur trueou qu’il n’existe aucun attribut Condition, la visualisation s’applique. Vous pouvez utiliser cet attribut pour la logique if-else dans les entrées de visualisation.

Par exemple, la visualisation suivante comporte deux éléments DisplayString pour un type de pointeur intelligent. Lorsque le membre _Myptr est vide, la condition du premier élément DisplayString se résout en true, de sorte que le formulaire s’affiche. Lorsque le membre _Myptr n’est pas vide, la condition prend la valeur falseet le deuxième élément DisplayString s’affiche.

<Type Name="std::auto_ptr&lt;*&gt;">
  <DisplayString Condition="_Myptr == 0">empty</DisplayString>
  <DisplayString>auto_ptr {*_Myptr}</DisplayString>
  <Expand>
    <ExpandedItem>_Myptr</ExpandedItem>
  </Expand>
</Type>

Attributs IncludeView et ExcludeView

Les attributs IncludeView et ExcludeView spécifient des éléments à afficher ou non dans des affichages spécifiques. Par exemple, dans la spécification Natvis suivante de std::vector, la vue simple n’affiche pas les éléments [size] et [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>

Vous pouvez utiliser les attributs IncludeView et ExcludeView sur les types et sur les membres individuels.

Élément Version

L’élément Version étend une entrée de visualisation à un module et une version spécifiques. L’élément Version permet d’éviter les collisions de noms, de réduire les incompatibilités par inadvertance et d’autoriser différentes visualisations pour différentes versions de type.

Si un fichier d’en-tête commun utilisé par différents modules définit un type, la visualisation avec version s’affiche uniquement lorsque le type se trouve dans la version du module spécifiée.

Dans l’exemple suivant, la visualisation s’applique uniquement au type DirectUI::Border trouvé dans le Windows.UI.Xaml.dll de la version 1.0 à la version 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>

Vous n’avez pas besoin de Min et de Max. Ils sont des attributs facultatifs. Les caractères génériques ne sont pas pris en charge.

L’attribut Name est au format filename.ext, tel que hello.exe ou some.dll. Aucun nom de chemin d’accès n’est autorisé.

Élément DisplayString

L’élément DisplayString spécifie une chaîne à afficher comme valeur d’une variable. Il accepte les chaînes arbitraires mélangées à des expressions. Tout ce qui se trouve entre des accolades est interprété comme une expression. Par exemple, l’entrée DisplayString suivante :

<Type Name="CPoint">
  <DisplayString>{{x={x} y={y}}}</DisplayString>
</Type>

Signifie que les variables de type CPoint s’affichent comme dans cette illustration :

Utiliser un élément DisplayString

Dans l’expression DisplayString, x et y, qui sont membres de CPoint, sont placés entre accolades, donc leurs valeurs sont évaluées. L’exemple montre également comment échapper une accolade en utilisant des accolades doubles ({{ ou }}).

Remarque

L'élément DisplayString est le seul élément qui accepte des chaînes arbitraires et la syntaxe avec accolades. Tous les autres éléments de visualisation acceptent uniquement les expressions que le débogueur peut évaluer.

Élément StringView

L’élément StringView définit une valeur que le débogueur peut envoyer au visualiseur de texte intégré. Par exemple, étant donné la visualisation suivante pour le type de ATL::CStringT :

<Type Name="ATL::CStringT&lt;wchar_t,*&gt;">
  <DisplayString>{m_pszData,su}</DisplayString>
</Type>

L’objet CStringT s’affiche dans une fenêtre variable comme dans cet exemple :

Élément CStringT DisplayString

L’ajout d’un élément StringView indique au débogueur qu’il peut afficher la valeur sous forme de visualisation de texte.

<Type Name="ATL::CStringT&lt;wchar_t,*&gt;">
  <DisplayString>{m_pszData,su}</DisplayString>
  <StringView>m_pszData,su</StringView>
</Type>

Pendant le débogage, vous pouvez sélectionner l’icône loupe en regard de la variable, puis sélectionner visualiseur de texte pour afficher la chaîne vers laquelle m_pszData pointe.

données CStringT avec le visualiseur StringView

L’expression {m_pszData,su} inclut un spécificateur de format C++ su, pour afficher la valeur sous forme de chaîne Unicode. Pour plus d'informations, consultez les spécificateurs de format en C++ ,.

Élément Expand

Le nœud facultatif Expand personnalise les enfants d’un type visualisé quand vous développez le type dans une fenêtre de variable. Le nœud Expand accepte la liste des nœuds enfants qui définissent les éléments enfants.

  • Si un nœud Expand n’est pas spécifié dans une entrée de visualisation, les enfants utilisent les règles d’expansion par défaut.

  • Si un nœud Expand est spécifié sans aucun nœud enfant en dessous, le type n’est pas développé dans les fenêtres du débogueur.

Expansion d'éléments

L’élément Item est l’élément le plus simple et le plus courant dans un nœud Expand. Item définit un seul élément enfant. Par exemple, une classe CRect avec des champs top, left, rightet bottom a l’entrée de visualisation suivante :

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

Dans la fenêtre du débogueur, le type CRect ressemble à cet exemple :

CRect avec extension d’élément Item

Le débogueur évalue les expressions spécifiées dans les éléments Width et Height, et affiche les valeurs dans la colonne Valeur de la fenêtre variable.

Le débogueur crée automatiquement le nœud [Vue brute] pour chaque extension personnalisée. La capture d’écran précédente affiche le nœud [Affichage brut] développé, afin de montrer comment l'affichage brut par défaut de l'objet diffère de sa visualisation Natvis. L’extension par défaut crée une sous-arborescence pour la classe de base et répertorie tous les membres de données de la classe de base en tant qu’enfants.

Remarque

Si l'expression de l'élément pointe vers un type complexe, le nœud Élément peut être étendu.

Extension ArrayItems

Utilisez le nœud ArrayItems pour que le débogueur Visual Studio interprète le type en tant que tableau et affiche ses éléments individuels. La visualisation de std::vector est un bon exemple :

<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 std::vector montre ses éléments individuels quand il est développé dans la fenêtre de variables :

std::vector utilisant l'expansion ArrayItems

Le nœud ArrayItems doit avoir :

  • Une expression Size (qui doit prendre la valeur d’un entier) pour que le débogueur comprenne la longueur du tableau.
  • Expression ValuePointer qui pointe vers le premier élément (qui doit être un pointeur d’un type d’élément qui n’est pas void*).

La valeur par défaut de la limite inférieure du tableau est 0. Pour remplacer la valeur, utilisez un élément LowerBound. Les fichiers .natvis fournis avec Visual Studio ont des exemples.

Remarque

Vous pouvez utiliser l’opérateur [], par exemple vector[i], avec n’importe quelle visualisation de tableau unidimensionnel qui utilise ArrayItems, même si le type lui-même (par exemple, CATLArray) n’autorise pas cet opérateur.

Vous pouvez également spécifier des tableaux multidimensionnels. Dans ce cas, le débogueur a besoin d’un peu plus d’informations pour afficher correctement les éléments enfants :

<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 spécifie si le tableau est dans l’ordre par lignes ou par colonnes.
  • Rank spécifie le rang du tableau.
  • L’élément Size accepte le paramètre de $i implicite, qu’il remplace par l’index de dimension pour trouver la longueur du tableau dans cette dimension.
    • Dans l’exemple précédent, l’expression _M_extent.M_base[0] doit donner la longueur de la 0ème dimension, _M_extent._M_base[1] la première, et ainsi de suite.
  • La LowerBound spécifie la limite inférieure de chaque dimension du tableau. Pour les tableaux multidimensionnels, vous pouvez spécifier une expression qui utilise le paramètre de $i implicite. Le paramètre $i sera remplacé par l’index de dimension pour trouver la limite inférieure du tableau dans cette dimension.
    • Dans l’exemple précédent, toutes les dimensions commencent à 0. Toutefois, si vous aviez ($i == 1) ? 1000 : 100 comme limite inférieure, la 0ème dimension commence à 100, et la première dimension commence à 1 000.
      • , comme [100, 1000], [100, 1001], [100, 1002], ... [101, 1000], [101, 1001],...

Voici comment un objet Concurrency::array bidimensionnel ressemble dans la fenêtre du débogueur :

Tableau à deux dimensions avec extension ArrayItems

Extension IndexListItems

Vous pouvez utiliser ArrayItems extension uniquement si les éléments de tableau sont disposés contigus en mémoire. Le débogueur accède à l’élément suivant en incrémentant simplement son pointeur. Si vous devez manipuler l'index du nœud de valeur, utilisez des nœuds IndexListItems. Voici une visualisation avec un nœud IndexListItems :

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

La seule différence entre ArrayItems et IndexListItems est le ValueNode, qui attend l’expression complète de l’élément ième avec le paramètre $i implicite.

Remarque

Vous pouvez utiliser l’opérateur [], par exemple vector[i], avec n’importe quelle visualisation de tableau unidimensionnel qui utilise IndexListItems, même si le type lui-même (par exemple, CATLArray) n’autorise pas cet opérateur.

Extension de LinkedListItems

Si le type visualisé représente une liste liée, le débogueur peut afficher ses enfants à l'aide d'un nœud LinkedListItems. La visualisation suivante pour le type de CAtlList utilise 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’élément Size fait référence à la longueur de la liste. HeadPointer pointe vers le premier élément, NextPointer fait référence à l’élément suivant et ValueNode fait référence à la valeur de l’élément.

Le débogueur évalue les expressions NextPointer et ValueNode dans le contexte de l’élément de nœud LinkedListItems, et non le type de liste parent. Dans l’exemple précédent, CAtlList a une classe CNode (trouvée dans atlcoll.h) qui est un nœud de la liste liée. m_pNext et m_element sont des champs de cette classe CNode, et non de la classe CAtlList.

ValueNode pouvez être laissé vide ou utiliser this pour faire référence au nœud LinkedListItems lui-même.

Élargissement des éléments de liste personnalisés

L’extension CustomListItems vous permet d’écrire une logique personnalisée pour parcourir une structure de données telle qu’une table de hachage. Utilisez CustomListItems pour visualiser les structures de données qui peuvent utiliser des expressions C++ pour tout ce dont vous avez besoin pour évaluer, mais ne correspondent pas tout à fait au moule pour ArrayItems, IndexListItemsou LinkedListItems.

Vous pouvez utiliser Exec pour exécuter du code à l’intérieur d’une extension CustomListItems, à l’aide des variables et des objets définis dans l’extension. Vous pouvez utiliser des opérateurs logiques, des opérateurs arithmétiques et des opérateurs d’affectation avec Exec. Vous ne pouvez pas utiliser Exec pour évaluer les fonctions, à l’exception des fonctions intrinsèques du débogueur prises en charge par l’évaluateur d’expression C++.

Le visualiseur suivant pour CAtlMap est un excellent exemple où CustomListItems convient.

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

Extension de TreeItems

Si le type visualisé représente une arborescence, le débogueur peut la parcourir et afficher ses enfants à l'aide d'un nœud TreeItems. Voici la visualisation du type std::map à l’aide d’un nœud TreeItems :

<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 syntaxe est similaire au nœud LinkedListItems. LeftPointer, RightPointeret ValueNode sont évalués dans le contexte de la classe de nœud d’arborescence. ValueNode pouvez être laissé vide ou utiliser this pour faire référence au nœud TreeItems lui-même.

Extension ExpandedItem

L’élément ExpandedItem génère une vue enfant agrégée en affichant les propriétés des classes de base ou des membres de données comme s’ils étaient des enfants du type visualisé. Le débogueur évalue l’expression spécifiée et ajoute les nœuds enfants du résultat à la liste enfant du type visualisé.

Par exemple, le type de pointeur intelligent auto_ptr<vector<int>> s’affiche généralement comme suit :

auto_ptr<vecteur<int>> expansion par défaut

Pour afficher les valeurs du vecteur, vous devez explorer deux niveaux dans la fenêtre variable, en passant par le membre _Myptr. En ajoutant un élément ExpandedItem, vous pouvez éliminer la variable _Myptr de la hiérarchie et afficher directement les éléments vectoriels :

<Type Name="std::auto_ptr&lt;*&gt;">
  <DisplayString>auto_ptr {*_Myptr}</DisplayString>
  <Expand>
    <ExpandedItem>_Myptr</ExpandedItem>
  </Expand>
</Type>

Extension ExpandedItem auto_ptr<vector<int>>

L’exemple suivant montre comment agréger des propriétés à partir de la classe de base dans une classe dérivée. Supposons que la classe CPanel dérive de CFrameworkElement. Au lieu de répéter les propriétés provenant de la classe de base CFrameworkElement, la visualisation de nœud ExpandedItem ajoute ces propriétés à la liste enfant de la classe CPanel.

<Type Name="CPanel">
  <DisplayString>{{Name = {*(m_pstrName)}}}</DisplayString>
  <Expand>
    <Item Name="IsItemsHost">(bool)m_bItemsHost</Item>
    <ExpandedItem>*(CFrameworkElement*)this,nd</ExpandedItem>
  </Expand>
</Type>

Le spécificateur de format nd, qui désactive la correspondance de visualisation pour la classe dérivée, est nécessaire ici. Dans le cas contraire, l’expression *(CFrameworkElement*)this entraînerait l’application de la visualisation CPanel, car les règles de correspondance de type de visualisation par défaut la considèrent comme la plus appropriée. Utilisez le spécificateur de format nd pour indiquer au débogueur d’utiliser la visualisation de classe de base ou l’expansion par défaut si la classe de base n’a aucune visualisation.

Extension de l’élément Synthetic

Bien que l’élément ExpandedItem fournit une vue plus plate des données en éliminant les hiérarchies, le nœud Synthetic effectue l’inverse. Il vous permet de créer un élément enfant artificiel qui n’est pas le résultat d’une expression. L’élément artificiel peut avoir ses propres éléments enfants. Dans l’exemple suivant, la visualisation du type Concurrency::array utilise un nœud Synthetic pour afficher un message de diagnostic à l’utilisateur :

<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 avec extension de l’élément Synthetic

Expansion intrinsèque

Fonction intrinsèque personnalisée qui peut être appelée à partir d’une expression. Un élément <Intrinsic> doit être accompagné d’un composant de débogueur qui implémente la fonction via l’interface IDkmIntrinsicFunctionEvaluator140. Pour plus d’informations sur l’implémentation d’une fonction intrinsèque personnalisée, consultez Implémenter une fonction intrinsèque personnalisée NatVis.

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

Élément HResult

L’élément HResult vous permet de personnaliser les informations affichées pour une HRESULT dans les fenêtres du débogueur. L’élément HRValue doit contenir la valeur 32 bits du HRESULT à personnaliser. L’élément HRDescription contient les informations à afficher dans la fenêtre du débogueur.


<HResult Name="MY_E_COLLECTION_NOELEMENTS">
  <HRValue>0xABC0123</HRValue>
  <HRDescription>No elements in the collection.</HRDescription>
</HResult>

Élément UIVisualizer

Un élément UIVisualizer permet d'inscrire un plug-in de visualiseur graphique auprès du débogueur. Un visualiseur graphique crée une boîte de dialogue ou une autre interface qui affiche une variable ou un objet d’une manière cohérente avec son type de données. Le plug-in du visualiseur doit être créé en tant que VSPackage et doit exposer un service consommable par le débogueur. Le fichier .natvis contient des informations d’inscription pour le plug-in, telles que son nom, l’identificateur global unique (GUID) du service exposé et les types qu’il peut visualiser.

Voici un exemple d’élément 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>
  • Une paire d’attributs ServiceId - Id identifie un UIVisualizer. Le ServiceId est le GUID du service que le package visualiseur expose. Id est un identificateur unique qui différencie les visualiseurs, si un service en fournit plusieurs. Dans l’exemple précédent, le même service de visualiseur fournit deux visualiseurs.

  • L’attribut MenuName définit un nom de visualiseur à afficher dans la liste déroulante en regard de l’icône de loupe dans le débogueur. Par exemple:

    Menu contextuel UIVisualizer

Chaque type défini dans le fichier .natvis doit répertorier explicitement les visualiseurs d’interface utilisateur qui peuvent l’afficher. Le débogueur fait correspondre les références du visualiseur des entrées de type aux visualiseurs inscrits. Par exemple, l’entrée de type suivante pour std::vector fait référence à la UIVisualizer dans l’exemple précédent.

<Type Name="std::vector&lt;int,*&gt;">
  <UIVisualizer ServiceId="{5452AFEA-3DF6-46BB-9177-C0B08F318025}" Id="1" />
</Type>

Vous pouvez voir un exemple de UIVisualizer dans l’extension Image Watch utilisée pour afficher des bitmaps en mémoire.

Élément CustomVisualizer

CustomVisualizer est un point d’extensibilité qui spécifie une extension VSIX que vous écrivez pour contrôler les visualisations dans Visual Studio Code. Pour plus d’informations sur l’écriture d’extensions VSIX, consultez le Visual Studio SDK.

Il est beaucoup plus important d’écrire un visualiseur personnalisé qu’une définition Natvis XML, mais vous êtes libre de contraintes sur ce que Natvis fait ou ne prend pas en charge. Les visualiseurs personnalisés ont accès à l’ensemble complet d’API d’extensibilité du débogueur, qui peuvent interroger et modifier le processus de débogage ou communiquer avec d’autres parties de Visual Studio.

Vous pouvez utiliser les attributs Condition, IncludeViewet ExcludeView sur les éléments CustomVisualizer.

Limitations

Les personnalisations Natvis fonctionnent avec des classes et des structs, mais pas des typedefs.

Natvis ne prend pas en charge les visualiseurs pour les types primitifs (par exemple, int, bool) ou pour les pointeurs vers des types primitifs. Dans ce scénario, une option consiste à utiliser le spécificateur de format approprié à votre cas d’usage. Par exemple, si vous utilisez double* mydoublearray dans votre code, vous pouvez utiliser un spécificateur de format de tableau dans la fenêtre Watch du débogueur, telle que l’expression mydoublearray, [100], qui affiche les 100 premiers éléments.