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.
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.
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 :
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.
W oknie dialogowym Dodawanie nowego elementu wybierz plik wizualizacji Visual C++>Utility>Debugger (.natvis).
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:
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.
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.
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>
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:
Wszystkie pliki natvis osadzone w debugowaniu pliku .pdb , chyba że plik o tej samej nazwie istnieje w załadowanym projekcie.
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.
Wszystkie pliki natvis zainstalowane i zarejestrowane za pośrednictwem pakietu VSIX.
- Katalog Natvis specyficzny dla użytkownika (na przykład %USERPROFILE%\Documents\Visual Studio 2022\Visualizers).
- Katalog Natvis specyficzny dla użytkownika (na przykład %USERPROFILE%\Documents\Visual Studio 2019\Visualizers).
- 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 \>\> 24),x\</Item\>
\<Item Name="HiByteStatus" Condition="(_flags \& 0xFF000000) == 0"\>"None"\</Item\>
\<Item Name="HiByteStatus" Condition="(_flags \& 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<*>">
<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):
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:
Jakiego typu wizualizacja powinna być używana (atrybut).
Name
Jak powinien wyglądać wartość obiektu tego typu (
DisplayString
element).Jak powinny wyglądać elementy członkowskie typu, gdy użytkownik rozszerza typ w oknie zmiennej (węzeł).
Expand
Klasy szablonowe
Atrybut Name
Type
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<*>">
<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
, , MediumLow
Medium
, MediumHigh
i 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<*>" 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>
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 true
wartość , 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<*>">
<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::vector
simple
Natvis widoku nie są wyświetlane [size]
elementy i [capacity]
.
<Type Name="std::vector<*>">
<DisplayString>{{ size={_Mylast - _Myfirst} }}</DisplayString>
<Expand>
<Item Name="[size]" ExcludeView="simple">_Mylast - _Myfirst</Item>
<Item Name="[capacity]" ExcludeView="simple">_Myend - _Myfirst</Item>
<ArrayItems>
<Size>_Mylast - _Myfirst</Size>
<ValuePointer>_Myfirst</ValuePointer>
</ArrayItems>
</Expand>
</Type>
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::Border
Windows.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:
W wyrażeniu DisplayString
x
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<wchar_t,*>">
<DisplayString>{m_pszData,su}</DisplayString>
</Type>
Obiekt CStringT
jest wyświetlany w oknie zmiennej w następujący przykład:
StringView
Dodanie elementu informuje debuger, że może wyświetlić wartość jako wizualizację tekstową.
<Type Name="ATL::CStringT<wchar_t,*>">
<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.
{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
, right
i 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:
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<*>">
<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:
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 nievoid*
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<*,*>">
<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.
- W poprzednim przykładzie wyrażenie
- 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],...
- , na przykład
- W poprzednim przykładzie wszystkie wymiary zaczynają się od 0. Jeśli
Oto jak obiekt dwuwymiarowy Concurrency::array
wygląda w oknie debugera:
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<*>">
<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.ValueNode
IndexListItems
ArrayItems
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<*,*>">
<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.h
pliku ), 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 , ArrayItems
IndexListItems
lub 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<*,*,*,*>">
<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>
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<*>">
<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
, RightPointer
i 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:
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<*>">
<DisplayString>auto_ptr {*_Myptr}</DisplayString>
<Expand>
<ExpandedItem>_Myptr</ExpandedItem>
</Expand>
</Type>
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<*,*>">
<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>
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<*>">
<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 elementUIVisualizer
. ToServiceId
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:
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<int,*>">
<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ć Condition
atrybutów , IncludeView
i 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.