Tworzenie niestandardowych widoków obiektów języka C++ w debugerze przy użyciu struktury Natvis

Struktura Natvis programu Visual Studio dostosowuje sposób wyświetlania typów natywnych w oknach zmiennych debugera, takich jak okna Locals i Watch oraz w obszarze Dane Wskazówki. Wizualizacje Natvis mogą pomóc w uwidocznieniu typów tworzonych podczas debugowania.

Natvis zastępuje plik autoexp.dat we wcześniejszych wersjach programu Visual Studio składnią XML, lepszą diagnostyką, przechowywaniem wersji i obsługą wielu plików.

Uwaga

Dostosowania natvis działają z klasami i strukturami, ale nie definicjami typów.

Wizualizacje natvis

Za pomocą struktury Natvis można tworzyć reguły wizualizacji dla tworzonych typów, aby deweloperzy mogli je łatwiej zobaczyć podczas debugowania.

Na przykład poniższa ilustracja przedstawia zmienną typu Windows::UI::XAML::Controls::TextBox w oknie debugera bez zastosowanych wizualizacji niestandardowych.

Domyślna wizualizacja textBox

Wyróżniony wiersz pokazuje Text właściwość TextBox klasy. Hierarchia klas złożonych utrudnia znalezienie tej właściwości. Debuger nie wie, jak interpretować typ ciągu niestandardowego, więc nie można zobaczyć ciągu przechowywanego w polu tekstowym.

To samo TextBox wygląda znacznie łatwiej w oknie zmiennej, gdy są stosowane niestandardowe reguły wizualizatora natvis. Ważne elementy członkowskie klasy są wyświetlane razem, a debuger pokazuje podstawową wartość ciągu niestandardowego typu ciągu.

Dane textBox przy użyciu wizualizatora

Używanie plików natvis w projektach języka C++

Usługa Natvis używa plików natvis do określania reguł wizualizacji. Plik natvis jest plikiem XML z rozszerzeniem natvis. Schemat Natvis jest zdefiniowany w <folderze> instalacyjnym programu VS\Xml\Schemas\1033\natvis.xsd.

Podstawowa struktura pliku natvis to jeden lub więcej Type elementów reprezentujących wpisy wizualizacji. W pełni kwalifikowana nazwa każdego Type elementu jest określona w jego Name atrybucie.

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

Program Visual Studio udostępnia niektóre pliki natvis w <folderze> instalacyjnym programu VS\Common7\Packages\Debugger\Visualizers . Te pliki mają reguły wizualizacji dla wielu typowych typów i mogą służyć jako przykłady pisania wizualizacji dla nowych typów.

Dodawanie pliku natvis do projektu C++

Plik natvis można dodać do dowolnego projektu C++.

Aby dodać nowy plik natvis :

  1. Wybierz węzeł projektu C++ w Eksplorator rozwiązań, a następnie wybierz pozycję Project Add new item (Dodaj nowy element)>lub kliknij projekt prawym przyciskiem myszy i wybierz polecenie Dodaj>nowy element.

    Jeśli nie widzisz wszystkich szablonów elementów, wybierz pozycję Pokaż wszystkie szablony.

  2. W oknie dialogowym Dodawanie nowego elementu wybierz plik wizualizacji Visual C++>Utility>Debugger (.natvis).

  3. Nadaj plikowi nazwę i wybierz pozycję Dodaj.

    Nowy plik zostanie dodany do Eksplorator rozwiązań i zostanie otwarty w okienku dokumentów programu Visual Studio.

Debuger programu Visual Studio ładuje pliki natvis w projektach języka C++ automatycznie i domyślnie uwzględnia je również w pliku .pdb podczas kompilacji projektu. W przypadku debugowania utworzonej aplikacji debuger ładuje plik natvis z pliku .pdb , nawet jeśli nie masz otwartego projektu. Jeśli nie chcesz, aby plik natvis zawarty w pliku .pdb był wyklucz go z skompilowany plik .pdb .

Aby wykluczyć plik natvis z pliku .pdb:

  1. Wybierz plik natvis w Eksplorator rozwiązań, a następnie wybierz ikonę Właściwości lub kliknij prawym przyciskiem myszy plik i wybierz pozycję Właściwości.

  2. Lista rozwijana strzałki obok pozycji Wykluczone z kompilacji i wybierz pozycję Tak, a następnie wybierz przycisk OK.

Uwaga

W przypadku debugowania projektów wykonywalnych użyj elementów rozwiązania, aby dodać wszystkie pliki natvis, które nie znajdują się w pliku .pdb, ponieważ nie ma dostępnego projektu C++.

Uwaga

Reguły natvis ładowane z pliku .pdb mają zastosowanie tylko do typów w modułach, do których odwołuje się plik .pdb . Jeśli na przykład moduł Module1.pdb ma wpis Natvis dla typu o nazwie Test, dotyczy Test tylko klasy w Module1.dll. Jeśli inny moduł definiuje również klasę o nazwie Test, wpis Module1.pdb Natvis nie ma do niego zastosowania.

Aby zainstalować i zarejestrować plik natvis za pomocą pakietu VSIX:

Pakiet VSIX może instalować i rejestrować pliki natvis . Niezależnie od tego, gdzie są zainstalowane, wszystkie zarejestrowane pliki natvis są automatycznie pobierane podczas debugowania.

  1. Uwzględnij plik natvis w pakiecie VSIX. Na przykład dla następującego pliku projektu:

    <?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. Zarejestruj plik natvis w pliku 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>
    

Lokalizacje plików Natvis

Możesz dodać pliki natvis do katalogu użytkownika lub do katalogu systemowego, jeśli chcesz, aby były one stosowane do wielu projektów.

Pliki natvis są oceniane w następującej kolejności:

  1. Wszystkie pliki natvis osadzone w debugowaniu pliku .pdb , chyba że plik o tej samej nazwie istnieje w załadowanym projekcie.

  2. Wszystkie pliki natvis , które znajdują się w załadowanym projekcie języka C++ lub rozwiązaniu najwyższego poziomu. Ta grupa obejmuje wszystkie załadowane projekty języka C++, w tym biblioteki klas, ale nie projekty w innych językach.

  3. Wszystkie pliki natvis zainstalowane i zarejestrowane za pośrednictwem pakietu VSIX.

  1. Katalog Natvis specyficzny dla użytkownika (na przykład %USERPROFILE%\Documents\Visual Studio 2022\Visualizers).
  1. Katalog Natvis specyficzny dla użytkownika (na przykład %USERPROFILE%\Documents\Visual Studio 2019\Visualizers).
  1. Katalog Natvis dla całego systemu (<Folder> instalacyjny programu Microsoft Visual Studio\Common7\Packages\Debugger\Visualizers). Ten katalog zawiera pliki natvis zainstalowane w programie Visual Studio. Jeśli masz uprawnienia administratora, możesz dodać pliki do tego katalogu.

Modyfikowanie plików natvis podczas debugowania

Plik natvis można zmodyfikować w środowisku IDE podczas debugowania projektu. Otwórz plik w tym samym wystąpieniu programu Visual Studio, za pomocą którego debugujesz, zmodyfikuj go i zapisz. Gdy tylko plik zostanie zapisany, w celu odzwierciedlenia zmiany zostaną zaktualizowane okna Watch and Locals .

Możesz również dodawać lub usuwać pliki natvis w rozwiązaniu, które debugujesz, a program Visual Studio dodaje lub usuwa odpowiednie wizualizacje.

Nie można zaktualizować plików natvis osadzonych w plikach .pdb podczas debugowania.

Jeśli zmodyfikujesz plik natvis poza programem Visual Studio, zmiany nie zostaną zastosowane automatycznie. Aby zaktualizować okna debugera, możesz ponownie sprawdzić polecenie natvisreload w oknie natychmiastowym . Następnie zmiany zostaną zastosowane bez ponownego uruchomienia sesji debugowania.

Użyj również polecenia natvisreload , aby uaktualnić plik natvis do nowszej wersji. Na przykład plik natvis może zostać zaewidencjonowany w kontroli źródła i chcesz pobrać ostatnie zmiany wprowadzone przez inną osobę.

Wyrażenia i formatowanie

Wizualizacje Natvis używają wyrażeń języka C++, aby określić elementy danych do wyświetlenia. Oprócz ulepszeń i ograniczeń wyrażeń języka C++ w debugerze, które są opisane w artykule Operator kontekstu (C++), należy pamiętać o następujących kwestiach:

  • Wyrażenia Natvis są oceniane w kontekście wizualizowanego obiektu, a nie bieżącej ramki stosu. Na przykład x w wyrażeniu Natvis odwołuje się do pola o nazwie x w wizualizowanym obiekcie, a nie do zmiennej lokalnej o nazwie x w bieżącej funkcji. Nie można uzyskać dostępu do zmiennych lokalnych w wyrażeniach natvis, chociaż można uzyskać dostęp do zmiennych globalnych.

  • Wyrażenia Natvis nie zezwalają na ocenę funkcji ani efekty uboczne. Wywołania funkcji i operatory przypisania są ignorowane. Ponieważ funkcje wewnętrzne debugera są wolne od efektów ubocznych, mogą być swobodnie wywoływane z dowolnego wyrażenia Natvis, mimo że inne wywołania funkcji są niedozwolone.

  • Aby kontrolować sposób wyświetlania wyrażenia, można użyć dowolnego specyfikatora formatu opisanego w specyfikatorach formatu w języku C++. Specyfikatory formatu są ignorowane, gdy wpis jest używany wewnętrznie przez usługę Natvis, na przykład Size wyrażenie w rozszerzeniu ArrayItems.

Uwaga

Ponieważ dokument Natvis to XML, wyrażenia nie mogą bezpośrednio używać operatorów ampersand, większe niż, mniejsze niż lub shift. Te znaki należy ujedlić zarówno w treści elementu, jak i w instrukcjach warunku. Na przykład:
\<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\>

Widoki natvis

Można zdefiniować różne widoki Natvis w celu wyświetlania typów na różne sposoby. Na przykład poniżej przedstawiono wizualizację std::vector , która definiuje uproszczony widok o nazwie simple. DisplayString Elementy i ArrayItems są wyświetlane w widoku domyślnym i simple widoku, a [size] elementy i [capacity] nie są wyświetlane w simple widoku.

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

W oknie Obserwowanie użyj specyfikatora formatu widoku , aby określić widok alternatywny. Prosty widok jest wyświetlany jako vec,view(simple):

Okno zegarka z prostym widokiem

Błędy natvis

Gdy debuger napotka błędy we wpisie wizualizacji, ignoruje je. Wyświetla typ w postaci pierwotnej lub wybiera inną odpowiednią wizualizację. Możesz użyć diagnostyki Natvis, aby zrozumieć, dlaczego debuger zignorował wpis wizualizacji i wyświetlić podstawową składnię i analizować błędy.

Aby włączyć diagnostykę natvis:

  • W obszarze Opcje narzędzi>(lub Opcje debugowania) Okno danych wyjściowych debugowania>>ustaw wartość Komunikaty diagnostyczne natvis (tylko język C++)>na wartość Błąd, Ostrzeżenie lub Pełne, a następnie wybierz przycisk OK.

Błędy są wyświetlane w oknie Dane wyjściowe .

Dokumentacja składni natvis

Następujące elementy i atrybuty mogą być używane w pliku Natvis.

AutoVisualizer, element

Element AutoVisualizer jest węzłem głównym pliku natvis i zawiera atrybut przestrzeni nazw xmlns: .

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

Element AutoVisualizer może mieć elementy podrzędne Type, HResult, UIVisualizer i CustomVisualizer .

Typ, element

Podstawowy Type wygląd wygląda następująco:

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

Element Type określa:

  1. Jakiego typu wizualizacja powinna być używana (atrybut).Name

  2. Jak powinien wyglądać wartość obiektu tego typu ( DisplayString element).

  3. Jak powinny wyglądać elementy członkowskie typu, gdy użytkownik rozszerza typ w oknie zmiennej (węzeł).Expand

Klasy szablonowe

Atrybut NameType elementu akceptuje gwiazdkę * jako symbol wieloznaczny, który może być używany dla nazw klas szablonów.

W poniższym przykładzie ta sama wizualizacja jest używana niezależnie od tego, czy obiekt jest obiektem CAtlArray<int>CAtlArray<float>, czy . Jeśli istnieje określony wpis wizualizacji dla elementu CAtlArray<float>, ma pierwszeństwo przed ogólnym.

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

Parametry szablonu można odwołać w wpisie wizualizacji przy użyciu makr $T 1, $T 2 itd. Aby znaleźć przykłady tych makr, zobacz pliki natvis dostarczane z programem Visual Studio.

Dopasowywanie typów wizualizatora

Jeśli nie można zweryfikować wpisu wizualizacji, zostanie użyta następna dostępna wizualizacja.

Atrybut możliwy do dziedziczenia

Opcjonalny Inheritable atrybut określa, czy wizualizacja ma zastosowanie tylko do typu podstawowego, czy do typu podstawowego i wszystkich typów pochodnych. Wartość domyślna Inheritable to true.

W poniższym przykładzie wizualizacja ma zastosowanie tylko do BaseClass typu:

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

Atrybut priorytetu

Opcjonalny Priority atrybut określa kolejność używania definicji alternatywnych, jeśli nie można przeanalizować definicji. Możliwe wartości Priority to: Low, , MediumLowMedium, MediumHighi High. Domyślna wartość to Medium. Atrybut Priority rozróżnia tylko priorytety w ramach tego samego pliku natvis .

Poniższy przykład najpierw analizuje wpis zgodny z biblioteką STL 2015. Jeśli to nie powiedzie się, użyje alternatywnego wpisu dla wersji 2013 biblioteki 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>

Atrybut opcjonalny

Atrybut można umieścić Optional w dowolnym węźle. Jeśli nie można przeanalizować podexpressionu wewnątrz opcjonalnego węzła, debuger ignoruje ten węzeł, ale stosuje pozostałe Type reguły. W poniższym typie [State] nie jest opcjonalny, ale [Exception] jest opcjonalny. Jeśli MyNamespace::MyClass ma pole o nazwie _M_exceptionHolder, zostanie wyświetlone zarówno [State] węzeł, jak i [Exception] węzeł, ale jeśli nie _M_exceptionHolder ma pola, zostanie wyświetlony tylko [State] węzeł.

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

Atrybut warunku

Opcjonalny Condition atrybut jest dostępny dla wielu elementów wizualizacji i określa, kiedy należy użyć reguły wizualizacji. Jeśli wyrażenie wewnątrz atrybutu warunku zostanie rozpoznane jako false, reguła wizualizacji nie ma zastosowania. Jeśli zostanie obliczona truewartość , lub nie ma Condition atrybutu, wizualizacja ma zastosowanie. Tego atrybutu można użyć dla logiki if-else w wpisach wizualizacji.

Na przykład poniższa wizualizacja zawiera dwa DisplayString elementy typu inteligentnego wskaźnika. _Myptr Gdy element członkowski jest pusty, warunek pierwszego DisplayString elementu jest rozpoznawany jako true, tak aby formularz był wyświetlany. Gdy element członkowski _Myptr nie jest pusty, warunek daje wartość false, a drugi DisplayString element jest wyświetlany.

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

Atrybuty IncludeView i ExcludeView

Atrybuty IncludeView i ExcludeView określają elementy do wyświetlania lub nie są wyświetlane w określonych widokach. Na przykład w poniższej specyfikacji std::vectorsimple Natvis widoku nie są wyświetlane [size] elementy i [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>

Atrybuty i ExcludeView można używać IncludeView w typach i na poszczególnych elementach członkowskich.

Version, element

Element Version określa zakres wpisu wizualizacji do określonego modułu i wersji. Element Version pomaga uniknąć kolizji nazw, zmniejsza nieumyślne niezgodności i umożliwia różne wizualizacje dla różnych wersji typów.

Jeśli wspólny plik nagłówkowy używany przez różne moduły definiuje typ, wizualizacja wersji jest wyświetlana tylko wtedy, gdy typ znajduje się w określonej wersji modułu.

W poniższym przykładzie wizualizacja ma zastosowanie tylko dla typu znalezionego DirectUI::BorderWindows.UI.Xaml.dll w wersji od 1.0 do 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>

Nie potrzebujesz funkcji Min i Max. Są to atrybuty opcjonalne. Nie są obsługiwane żadne symbole wieloznaczne.

Atrybut Name ma format nazwa_pliku.ext, na przykład hello.exe lub some.dll. Nazwy ścieżek nie są dozwolone.

DisplayString, element

Element DisplayString określa ciąg, który ma być wyświetlany jako wartość zmiennej. Akceptuje dowolne ciągi mieszane z wyrażeniami. Wszystko wewnątrz nawiasów klamrowych jest interpretowane jako wyrażenie. Na przykład następujący DisplayString wpis:

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

Oznacza, że zmienne typu CPoint są wyświetlane jak na poniższej ilustracji:

Używanie elementu DisplayString

W wyrażeniu DisplayStringx i y, które są elementami członkowskimi CPoint, znajdują się wewnątrz nawiasów klamrowych, więc ich wartości są oceniane. W przykładzie pokazano również, jak można uniknąć nawiasu klamrowego przy użyciu podwójnych nawiasów klamrowych ( {{ lub }} ).

Uwaga

Element DisplayString jest jedynym elementem, który akceptuje dowolne ciągi i składnię nawiasów klamrowych. Wszystkie inne elementy wizualizacji akceptują tylko wyrażenia, które może ocenić debuger.

StringView, element

Element StringView definiuje wartość, którą debuger może wysyłać do wbudowanego wizualizatora tekstu. Na przykład, biorąc pod uwagę następującą wizualizację ATL::CStringT dla typu:

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

Obiekt CStringT jest wyświetlany w oknie zmiennej w następujący przykład:

CStringT DisplayString, element

StringView Dodanie elementu informuje debuger, że może wyświetlić wartość jako wizualizację tekstową.

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

Podczas debugowania można wybrać ikonę lupy obok zmiennej, a następnie wybrać pozycję Text Visualizer , aby wyświetlić ciąg, który m_pszData wskazuje.

Dane CStringT za pomocą wizualizatora StringView

{m_pszData,su} Wyrażenie zawiera specyfikator formatu C++ su, aby wyświetlić wartość jako ciąg Unicode. Aby uzyskać więcej informacji, zobacz Specyfikatory formatu w języku C++.

Rozwiń element

Opcjonalny Expand węzeł dostosowuje elementy podrzędne typu wizualizowanego podczas rozwijania typu w oknie zmiennej. Węzeł Expand akceptuje listę węzłów podrzędnych, które definiują elementy podrzędne.

  • Expand Jeśli węzeł nie jest określony we wpisie wizualizacji, elementy podrzędne używają domyślnych reguł rozszerzenia.

  • Expand Jeśli węzeł jest określony bez węzłów podrzędnych, typ nie jest rozszerzalny w oknach debugera.

Rozszerzanie elementu

Element Item jest najbardziej podstawowym i typowym elementem w węźle Expand . Item definiuje pojedynczy element podrzędny. Na przykład CRect klasa z polami top, left, righti bottom ma następujący wpis wizualizacji:

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

W oknie CRect debugera typ wygląda następująco:

CRect z rozszerzeniem elementu Item

Debuger oblicza wyrażenia określone w Width elementach i Height i i wyświetla wartości w kolumnie Wartość okna zmiennej.

Debuger automatycznie tworzy węzeł [Nieprzetworzonego widoku] dla każdego rozszerzenia niestandardowego. Powyższy zrzut ekranu przedstawia rozwinięty węzeł [Nieprzetworzony widok], aby pokazać, jak domyślny nieprzetworzony widok obiektu różni się od wizualizacji Natvis. Rozszerzenie domyślne tworzy poddrzewo dla klasy bazowej i wyświetla listę wszystkich elementów członkowskich danych klasy bazowej jako elementów podrzędnych.

Uwaga

Jeśli wyrażenie elementu elementu wskazuje typ złożony, węzeł Element można rozwinąć.

Rozszerzenie ArrayItems

Użyj węzła ArrayItems , aby debuger programu Visual Studio interpretował typ jako tablicę i wyświetlał poszczególne elementy. Wizualizacja jest std::vector dobrym przykładem:

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

Element A std::vector pokazuje poszczególne elementy po rozwinięciu w oknie zmiennej:

std::vector z rozszerzeniem ArrayItems

Węzeł ArrayItems musi mieć następujące elementy:

  • Wyrażenie Size (które musi obliczyć liczbę całkowitą) dla debugera, aby zrozumieć długość tablicy.
  • Wyrażenie ValuePointer wskazujące pierwszy element (który musi być wskaźnikiem typu elementu, który nie void*jest ).

Wartość domyślna dolnej granicy tablicy to 0. Aby zastąpić wartość, użyj LowerBound elementu . Pliki natvis dostarczane z programem Visual Studio zawierają przykłady.

Uwaga

Możesz użyć [] operatora, na przykład vector[i], z dowolną jednowymiarową wizualizacją tablicy, która używa ArrayItems, nawet jeśli sam typ (na przykład CATLArray) nie zezwala na ten operator.

Można również określić tablice wielowymiarowe. W takim przypadku debuger potrzebuje nieco więcej informacji, aby prawidłowo wyświetlać elementy podrzędne:

<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 Określa, czy tablica jest w kolejności głównej wiersza, czy kolumny głównej.
  • Rank określa rangę tablicy.
  • Element Size akceptuje niejawny $i parametr, który zastępuje indeksem wymiaru, aby znaleźć długość tablicy w tym wymiarze.
    • W poprzednim przykładzie wyrażenie _M_extent.M_base[0] powinno zawierać długość wymiaru 0, _M_extent._M_base[1] pierwszego i tak dalej.
  • Parametr LowerBound określa dolną granicę każdego wymiaru tablicy. W przypadku tablic wielowymiarowych można określić wyrażenie, które używa niejawnego $i parametru. Parametr $i zostanie zastąpiony indeksem wymiaru, aby znaleźć dolną granicę tablicy w tym wymiarze.
    • W poprzednim przykładzie wszystkie wymiary zaczynają się od 0. Jeśli ($i == 1) ? 1000 : 100 jednak jako dolna granica, wymiar 0 rozpocznie się od 100, a pierwszy wymiar rozpocznie się o 1000.
      • , na przykład [100, 1000], [100, 1001], [100, 1002], ... [101, 1000], [101, 1001],...

Oto jak obiekt dwuwymiarowy Concurrency::array wygląda w oknie debugera:

Tablica dwuwymiarowa z rozszerzeniem ArrayItems

Rozszerzenie IndexListItems

Rozszerzenia można używać ArrayItems tylko wtedy, gdy elementy tablicy są ułożone stale w pamięci. Debuger dostaje się do następnego elementu, po prostu zwiększając wskaźnik. Jeśli musisz manipulować indeksem w węźle wartości, użyj IndexListItems węzłów. Oto wizualizacja z węzłem 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>

Jedyną różnicą między elementami i jest , która oczekuje pełnego wyrażenia na element i z niejawnego $i parametru.ValueNodeIndexListItemsArrayItems

Uwaga

Możesz użyć [] operatora, na przykład vector[i], z dowolną jednowymiarową wizualizacją tablicy, która używa IndexListItems, nawet jeśli sam typ (na przykład CATLArray) nie zezwala na ten operator.

Rozszerzenie LinkedListItems

Jeśli wizualizowany typ reprezentuje połączoną listę, debuger może wyświetlić jego elementy podrzędne przy użyciu węzła LinkedListItems . Poniższa wizualizacja dla CAtlList typu używa elementu 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>

Element Size odnosi się do długości listy. HeadPointer wskazuje pierwszy element, NextPointer odwołuje się do następnego elementu i ValueNode odwołuje się do wartości elementu.

Debuger oblicza NextPointer wyrażenia i ValueNode w kontekście LinkedListItems elementu node, a nie nadrzędnego typu listy. W poprzednim przykładzie CAtlList ma klasę (znajdującą CNode się w atlcoll.hpliku ), która jest węzłem połączonej listy. m_pNext i m_element są polami tej CNode klasy, a nie z CAtlList klasy.

ValueNode można pozostawić puste lub użyć this polecenia , aby odwołać się do samego węzła LinkedListItems .

Rozszerzenie CustomListItems

Rozszerzenie CustomListItems umożliwia pisanie niestandardowej logiki przechodzenia przez strukturę danych, taką jak tabela skrótu. Służy CustomListItems do wizualizowania struktur danych, które mogą używać wyrażeń języka C++ dla wszystkich elementów, które należy ocenić, ale nie pasują do formy , ArrayItemsIndexListItemslub LinkedListItems.

Możesz użyć Exec polecenia , aby wykonać kod wewnątrz CustomListItems rozszerzenia, używając zmiennych i obiektów zdefiniowanych w rozszerzeniu. Można użyć operatorów logicznych, operatorów arytmetycznych i operatorów przypisania za pomocą polecenia Exec. Nie można użyć Exec funkcji do oceny, z wyjątkiem funkcji wewnętrznych debugera obsługiwanych przez ewaluatora wyrażeń języka C++.

Poniższy wizualizator jest CAtlMap doskonałym przykładem, w którym CustomListItems jest to odpowiednie.

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

Rozszerzenie TreeItems

Jeśli wizualizowany typ reprezentuje drzewo, debuger może przejść przez drzewo i wyświetlić jego elementy podrzędne przy użyciu węzła TreeItems . Oto wizualizacja typu std::map używająca węzła 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>

Składnia jest podobna do węzła LinkedListItems . LeftPointer, RightPointeri ValueNode są oceniane w kontekście klasy węzła drzewa. ValueNode można pozostawić puste lub użyć this polecenia , aby odwołać się do samego węzła TreeItems .

Rozszerzenie ExpandedItem

Element ExpandedItem generuje zagregowany widok podrzędny, wyświetlając właściwości klas bazowych lub składowych danych tak, jakby były elementami podrzędnymi wizualizowanego typu. Debuger oblicza określone wyrażenie i dołącza węzły podrzędne wyniku do listy podrzędnej zwizualizowanego typu.

Na przykład typ auto_ptr<vector<int>> inteligentnego wskaźnika zwykle jest wyświetlany jako:

<auto_ptr wektor<int>> rozszerzenia domyślnego

Aby wyświetlić wartości wektora, należy przejść do szczegółów dwóch poziomów w oknie zmiennej, przechodząc przez element członkowski _Myptr . Dodając element, można wyeliminować zmienną ExpandedItem_Myptr z hierarchii i bezpośrednio wyświetlić elementy wektorów:

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

<auto_ptr wektor<int>> ExpandedItem

W poniższym przykładzie pokazano, jak agregować właściwości z klasy bazowej w klasie pochodnej. Załóżmy, CPanel że klasa pochodzi z klasy CFrameworkElement. Zamiast powtarzać właściwości pochodzące z klasy bazowej CFrameworkElement , ExpandedItem wizualizacja węzła dołącza te właściwości do listy podrzędnej CPanel klasy.

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

Specyfikator formatu drugiego , który wyłącza dopasowywanie wizualizacji dla klasy pochodnej, jest konieczne tutaj. W przeciwnym razie wyrażenie *(CFrameworkElement*)this spowoduje CPanel ponowne zastosowanie wizualizacji, ponieważ domyślne reguły dopasowywania typów wizualizacji uznają ją za najbardziej odpowiednią. Użyj specyfikatora formatu nd , aby poinstruować debugera, aby używał wizualizacji klasy bazowej lub domyślne rozszerzenie, jeśli klasa bazowa nie ma wizualizacji.

Rozszerzenie elementu syntetycznego

ExpandedItem Chociaż element zapewnia bardziej pochlebny widok danych, eliminując hierarchie, Synthetic węzeł wykonuje odwrotne działania. Umożliwia utworzenie sztucznego elementu podrzędnego, który nie jest wynikiem wyrażenia. Sztuczny element może mieć własne elementy podrzędne. W poniższym przykładzie wizualizacja typu Concurrency::array używa Synthetic węzła do wyświetlania użytkownikowi komunikatu diagnostycznego:

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

Współbieżność::Tablica z rozszerzeniem elementu syntetycznego

Rozszerzanie instrynacji instrygicznej

Niestandardowa funkcja wewnętrzna, którą można wywołać z wyrażenia. Element <Intrinsic> musi być dołączony przez składnik debugera, który implementuje funkcję za pośrednictwem interfejsu 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>

HResult, element

Element HResult umożliwia dostosowanie informacji wyświetlanych dla elementu HRESULT w oknach debugera. Element HRValue musi zawierać wartość 32-bitową HRESULT , która ma zostać dostosowana. Element HRDescription zawiera informacje, które mają być wyświetlane w oknie debugera.


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

UIVisualizer, element

Element UIVisualizer rejestruje wtyczkę wizualizatora graficznego w debugerze. Wizualizator graficzny tworzy okno dialogowe lub inny interfejs, który pokazuje zmienną lub obiekt w sposób spójny z typem danych. Wtyczka wizualizatora musi być utworzona jako pakiet VSPackage i musi uwidocznić usługę, którą może używać debuger. Plik natvis zawiera informacje rejestracyjne dla wtyczki, takie jak jego nazwa, unikatowy identyfikator globalny (GUID) uwidocznionej usługi i typy, które może wizualizować.

Oto przykład elementu interfejsu użytkownikaVisualizer:

<?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>
  • Para ServiceId - Id atrybutów identyfikuje element UIVisualizer. To ServiceId identyfikator GUID usługi uwidacznia pakiet wizualizatora. Id jest unikatowym identyfikatorem, który rozróżnia wizualizatory, jeśli usługa udostępnia więcej niż jeden. W poprzednim przykładzie ta sama usługa wizualizatora udostępnia dwa wizualizatory.

  • Atrybut MenuName definiuje nazwę wizualizatora do wyświetlenia na liście rozwijanej obok ikony lupy w debugerze. Na przykład:

    Menu skrótów menu interfejsu użytkownikaVisualizer

Każdy typ zdefiniowany w pliku natvis musi jawnie wyświetlić wszystkie wizualizatory interfejsu użytkownika, które mogą go wyświetlić. Debuger pasuje do odwołań wizualizatora w wpisach typu z zarejestrowanymi wizualizatorami. Na przykład następujący wpis typu dla std::vector odwołań UIVisualizer w poprzednim przykładzie.

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

Przykład można UIVisualizer zobaczyć w rozszerzeniu Image Watch używanym do wyświetlania map bitowych w pamięci.

CustomVisualizer, element

CustomVisualizer to punkt rozszerzalności, który określa rozszerzenie VSIX, które zapisujesz w celu kontrolowania wizualizacji w programie Visual Studio Code. Aby uzyskać więcej informacji na temat pisania rozszerzeń VSIX, zobacz zestaw Visual Studio SDK.

Pisanie niestandardowego wizualizatora niż definicja natvis XML jest o wiele bardziej pracochłonne, ale nie ma ograniczeń dotyczących tego, co robi natvis lub nie obsługuje. Niestandardowe wizualizatory mają dostęp do pełnego zestawu interfejsów API rozszerzalności debugera, które mogą wykonywać zapytania i modyfikować proces debugowania lub komunikować się z innymi częściami programu Visual Studio.

Możesz użyć Conditionatrybutów , IncludeViewi ExcludeView dla CustomVisualizer elementów.

Ograniczenia

Dostosowania natvis działają z klasami i strukturami, ale nie definicjami typów.

Natvis nie obsługuje wizualizatorów typów pierwotnych (na przykład int, bool) ani wskaźników do typów pierwotnych. W tym scenariuszu jedną z opcji jest użycie specyfikatora formatu odpowiedniego dla danego przypadku użycia. Jeśli na przykład używasz double* mydoublearray w kodzie, możesz użyć specyfikatora formatu tablicy w oknie Czujka debugera, takiego jak wyrażenie mydoublearray, [100], które pokazuje pierwsze 100 elementów.