Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Struktura Natvis programu Visual Studio dostosowuje sposób wyświetlania typów natywnych w oknach zmiennych debugera, takich jak Locals i Watch windows, a w DataTips. 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.
Nota
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 właściwość Text klasy TextBox. 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.
Ten sam TextBox wygląda znacznie prościej w oknie zmiennej, gdy stosowane są niestandardowe reguły wizualizatora Natvis. Ważni członkowie klasy są wyświetlani razem, a debugger pokazuje wartość ciągu bazowego niestandardowego typu znakowego.
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 instalacji programu <VS>\Xml\Schemas\1033\natvis.xsd.
Podstawowa struktura pliku .natvis składa się z jednego lub więcej elementów Type reprezentujących wpisy wizualizacji. W pełni kwalifikowana nazwa każdego elementu Type jest określona w atrybucie 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>
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++
Do dowolnego projektu C++ można dodać plik .natvis.
Aby dodać nowy plik .natvis:
Wybierz węzeł projektu C++ w eksploratorze rozwiązań i wybierz pozycję Project>Dodaj nowy elementlub kliknij projekt prawym przyciskiem myszy i wybierz Dodaj>Nowy element.
Jeśli nie widzisz wszystkich szablonów elementów, wybierz pozycję Pokaż wszystkie szablony.
W oknie dialogowym Dodawanie nowego elementu wybierz pozycję Visual C++>Utility>Debugger visualization file (natvis).
Nadaj plikowi nazwę i wybierz pozycję Dodaj.
Nowy plik zostanie dodany do eksploratora rozwiązań i zostanie otwarty w okienku dokumentów programu Visual Studio.
Debuger programu Visual Studio automatycznie ładuje pliki .natvis w projektach języka C++, a domyślnie dołącza je również podczas budowania projektu do pliku .pdb. 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 był uwzględniony w pliku .pdb, możesz wykluczyć go z wygenerowanego pliku .pdb.
Aby wykluczyć plik .natvis z .pdb:
Wybierz plik .natvis w eksploratorze rozwiązań , a następnie wybierz ikonę właściwości lub kliknij plik prawym przyciskiem myszy i wybierz Właściwości.
Kliknij strzałkę obok pozycji Wykluczone z Kompilacji i wybierz Tak, a następnie wybierz OK.
Nota
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++.
Nota
Reguły natvis ładowane z pliku .pdb mają zastosowanie wyłącznie do typów w modułach, do których odnosi się plik .pdb. Jeśli na przykład plik Module1.pdb ma wpis Natvis dla typu o nazwie Test, dotyczy to tylko klasy Test 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 pośrednictwem 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.
Dołącz 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, które są osadzone w .pdb, z którego dokonujesz debugowania, 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 obejmujący cały system (<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
Podczas debugowania projektu można zmodyfikować plik natvis w środowisku IDE. 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, okna Watch i Locals zaktualizują się, aby odzwierciedlić zmianę.
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 wykonać polecenie .natvisreload w oknie Natychmiastowym. Zmiany zostaną wprowadzone 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 Operator kontekstu (C++), należy pamiętać o następujących kwestiach:
Wyrażenia Natvis są obliczane w kontekście wizualizowanego obiektu, a nie bieżącej ramki stosu. Na przykład
xw wyrażeniu Natvis odwołuje się do pola o nazwie x 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 skutkó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 Specyfikatory formatu w języku C++. Specyfikatory formatu są ignorowane, gdy wpis jest używany wewnętrznie przez usługę Natvis, na przykład wyrażenie
Sizew rozszerzeniu ArrayItems.
Nota
Ponieważ dokument Natvis jest w formacie XML, wyrażenia nie mogą bezpośrednio używać znaków & (ampersand), > (większości niż), < (mniejszości niż) ani operatorów przesunięcia. Te znaki należy wyeksportować zarówno w treści elementu, jak i w wyrażeniach warunkowych. 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, aby wyświetlać typy na różne sposoby. Na przykład poniższy fragment kodu przedstawia wizualizację std::vector , która definiuje uproszczony widok o nazwie simple. Elementy DisplayString i ArrayItems są wyświetlane w widoku domyślnym i widoku simple, natomiast elementy [size] i [capacity] nie są wyświetlane w widoku simple.
<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 Watch użyj specyfikatora formatu „view” aby określić alternatywny widok. Prosty widok jest wyświetlany jako vec,view(simple):
okno 
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 oraz aby zobaczyć błędy składni i analizy parsującej.
Aby włączyć diagnostykę natvis:
Otwórz okienko Opcje narzędzi> i rozwiń sekcję Wszystkie ustawienia>>. Akcja
debugowania Opcje otwiera okienko w tej samej sekcji.W oknie danych wyjściowych, w sekcji Ogólne ustawienia danych wyjściowych, ustaw opcję Komunikaty diagnostyczne natvis (tylko C++) na Błąd, Ostrzeżenie lub Szczegółowe.
Otwórz okno dialogowe Opcje narzędzi> i rozwiń sekcję Debugowanie>. Akcja Debug>Opcje otwiera okno dialogowe w tej samej sekcji.
W oknie Dane wyjściowe>Ogólne ustawienia danych wyjściowych ustaw opcję Komunikaty diagnostyczne natvis (tylko C++) na Błąd, Ostrzeżenie lub Szczegółowe.
Kliknij przycisk OK.
Błędy są wyświetlane w oknie danych wyjściowych.
Odniesienie do 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ć Type, HResult, UIVisualizeri CustomVisualizer jako elementy podrzędne.
Element typu
Podstawowy przykład Type 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 wizualizację należy użyć (atrybut
Name).Jak powinna wyglądać wartość obiektu tego typu (element
DisplayString).Jak powinni wyglądać członkowie typu, gdy użytkownik rozwinie typ w oknie zmiennej (węzeł
Expand).
Klasy szablonowe
Atrybut Name elementu Type 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, czy obiekt jest CAtlArray<int>, czy CAtlArray<float>. Jeśli istnieje określony wpis wizualizacji dla CAtlArray<float>, ma pierwszeństwo przed ogólnym.
<Type Name="ATL::CAtlArray<*>">
<DisplayString>{{Count = {m_nSize}}}</DisplayString>
</Type>
Można odnosić się do parametrów szablonu w wpisie wizualizacji, używając makr $T1, $T2, 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 atrybut Inheritable 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 typu BaseClass:
<Type Name="Namespace::BaseClass" Inheritable="false">
<DisplayString>{{Count = {m_nSize}}}</DisplayString>
</Type>
Atrybut priorytetu
Opcjonalny atrybut Priority określa kolejność używania definicji alternatywnych, jeśli nie można przeanalizować definicji. Możliwe wartości Priority to: Low, MediumLow,Medium, MediumHighi High. Wartość domyślna to Medium. Atrybut Priority rozróżnia tylko priorytety w tym samym pliku .natvis.
Poniższy przykład najpierw analizuje wpis, który odpowiada wersji STL z 2015 roku. Jeśli analiza składniowa się nie powiedzie, użyje alternatywnego wpisu dla biblioteki STL z roku 2013.
<!-- 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 Optional można umieścić 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 reguły Type. W poniższym typie [State] nie jest opcjonalna, ale [Exception] jest opcjonalna. Jeśli MyNamespace::MyClass ma pole o nazwie _M_exceptionHolder, zostanie wyświetlone zarówno węzeł [State], jak i węzeł [Exception], ale jeśli nie ma pola _M_exceptionHolder, zostanie wyświetlony tylko węzeł [State].
<Type Name="MyNamespace::MyClass">
<Expand>
<Item Name="[State]">_M_State</Item>
<Item Name="[Exception]" Optional="true">_M_exceptionHolder</Item>
</Expand>
</Type>
Atrybut warunku
Opcjonalny atrybut Condition 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 wartość jest równa true, lub jeśli brak atrybutu Condition, wizualizacja ma zastosowanie. Tego atrybutu można użyć dla logiki if-else w wpisach wizualizacji.
Na przykład poniższa wizualizacja zawiera dwa elementy DisplayString dla inteligentnego wskaźnika. Gdy element członkowski _Myptr jest pusty, warunek pierwszego elementu DisplayString jest rozpoznawany jako true, tak aby formularz był wyświetlany. Gdy członek _Myptr nie jest pusty, warunek zostanie obliczony na false, a drugi element DisplayString zostanie wyświetlony.
<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 Natvis std::vectorwidok simple nie wyświetla elementów [size] 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>
Można użyć atrybutów IncludeView i ExcludeView na typach i poszczególnych członkach.
Element wersji
Element Version określa wpis wizualizacji dla 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 DirectUI::Border znalezionego w Windows.UI.Xaml.dll z wersji 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 zarówno Min, jak i Max. Są to atrybuty opcjonalne. Nie są obsługiwane żadne symbole wieloznaczne.
Atrybut Name jest w formacie nazwa pliku.ext, na przykład hello.exe lub some.dll. Nazwy ścieżek nie są dozwolone.
element DisplayString
Element DisplayString określa ciąg, który ma być wyświetlany jako wartość zmiennej. Akceptuje dowolne ciągi znaków łączone z wyrażeniami. Wszystko wewnątrz nawiasów klamrowych jest interpretowane jako wyrażenie. Na przykład następujący wpis DisplayString:
<Type Name="CPoint">
<DisplayString>{{x={x} y={y}}}</DisplayString>
</Type>
Oznacza, że zmienne typu CPoint są wyświetlane jak na tej ilustracji.
W wyrażeniu DisplayStringx i y, które są członkami CPoint, znajdują się wewnątrz nawiasów klamrowych, więc ich wartości są obliczane. W przykładzie pokazano również, jak można uniknąć nawiasu klamrowego przy użyciu podwójnych nawiasów klamrowych ( {{ lub }} ).
Nota
Element DisplayString jest jedynym, który akceptuje dowolne ciągi znaków oraz 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 wysłać do wbudowanego wizualizatora tekstu. Na przykład biorąc pod uwagę następującą wizualizację dla typu ATL::CStringT:
<Type Name="ATL::CStringT<wchar_t,*>">
<DisplayString>{m_pszData,su}</DisplayString>
</Type>
Obiekt CStringT jest wyświetlany w oknie zmiennych według następującego przykładu:
element 
Dodanie elementu StringView informuje debuger, że może wyświetlać 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ć Wizualizator tekstu, aby wyświetlić ciąg, na który wskazuje m_pszData.
Wyrażenie {m_pszData,su} 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 węzeł Expand 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.
Jeśli w wizualizacji nie określono węzła
Expand, elementy podrzędne stosują domyślne reguły rozszerzenia.Jeśli węzeł
Expandjest określony bez węzłów podrzędnych, typ nie jest rozszerzalny w oknach debugera.
Rozszerzenie elementu
Element Item jest najbardziej podstawowym i typowym elementem w węźle Expand.
Item definiuje pojedynczy element podrzędny. Na przykład klasa CRect 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 debugera typ CRect wygląda następująco:
Debuger oblicza wyrażenia określone w elementach Width i Height oraz wyświetla wartości w kolumnie Wartość okna zmiennej.
Debugger automatycznie tworzy węzeł [Widok surowy] dla każdego rozszerzenia niestandardowego. Na powyższym zrzucie ekranu przedstawiono węzeł [Widok nieprzetworzony] w rozwiniętej postaci, aby pokazać, jak domyślny widok nieprzetworzony obiektu różni się od jego 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.
Nota
Jeśli wyrażenie elementu odnosi się do typu złożonego, węzeł Item może być rozszerzany.
Rozszerzenie ArrayItems
Użyj węzła ArrayItems, aby debuger programu Visual Studio interpretował typ jako tablicę i wyświetlał poszczególne elementy. Wizualizacja std::vector jest 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>
Po rozwinięciu w oknie zmiennych, std::vector pokazuje swoje poszczególne elementy.
Węzeł ArrayItems musi mieć następujące elementy:
- Wyrażenie
Size(które musi zostać obliczone na liczbę całkowitą), aby debuger zrozumiał długość tablicy. - Wyrażenie
ValuePointerwskazujące pierwszy element (który musi być wskaźnikiem, ale nie może być typuvoid*).
Wartość domyślna dolnej granicy tablicy to 0. Aby zastąpić wartość, użyj elementu LowerBound. Pliki .natvis dostarczane z programem Visual Studio zawierają przykłady.
Nota
Możesz użyć operatora [], na przykład vector[i], z dowolną wizualizacją jednowymiarowej 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>
-
Directionokreśla, czy tablica jest w porządku wierszowym lub kolumnowym. -
Rankokreśla rangę tablicy. - Element
Sizeakceptuje niejawny parametr$i, 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 podać długość zerowego wymiaru,_M_extent._M_base[1]pierwszego, itd.
- W poprzednim przykładzie wyrażenie
-
LowerBoundokreśla dolną granicę każdego wymiaru tablicy. W przypadku tablic wielowymiarowych można określić wyrażenie, które używa niejawnego parametru$i. Parametr$ijest zastępowany indeksem wymiaru, aby znaleźć dolną granicę tablicy w tym wymiarze.- W poprzednim przykładzie wszystkie wymiary zaczynają się od 0. Jeśli jednak miałbyś
($i == 1) ? 1000 : 100jako dolną granicę, wymiar 0 zaczyna się od 100, a pierwszy wymiar zaczyna się od 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 jednak miałbyś
Oto jak dwuwymiarowy obiekt Concurrency::array wygląda w oknie debugera:
Rozszerzenie IndexListItems
Można użyć rozszerzenia ArrayItems tylko wtedy, gdy elementy tablicy są ułożone kolejno w pamięci. Debuger dostaje się do następnego elementu, po prostu zwiększając wskaźnik. Jeśli musisz manipulować indeksem prowadzącym do węzła wartości, użyj węzłów IndexListItems. 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 ArrayItems a IndexListItems jest ValueNode, która oczekuje pełnego wyrażenia do i-tego elementu z niejawnego parametru $i.
Nota
Możesz użyć operatora [], na przykład vector[i], z dowolną wizualizacją jednowymiarowej tablicy, która używa metody IndexListItems, nawet jeśli sam typ (na przykład CATLArray) nie zezwala na użycie tego operatora.
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 typu CAtlList używa 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, a ValueNode odnosi się do wartości elementu.
Debuger ocenia wyrażenia NextPointer i ValueNode w kontekście elementu węzła LinkedListItems, a nie nadrzędnego typu listy. W poprzednim przykładzie CAtlList ma klasę CNode (znaleziono w atlcoll.h), która jest węzłem połączonej listy.
m_pNext i m_element to pola tej klasy CNode, a nie klasy CAtlList.
ValueNode można pozostawić puste lub użyć this, 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. Użyj CustomListItems, aby wizualizować struktury danych, które mogą używać wyrażeń języka C++ dla wszystkich elementów potrzebnych do oceny, ale nie pasują do formy ArrayItems, IndexListItemslub LinkedListItems.
Za pomocą Exec można wykonywać kod wewnątrz rozszerzenia CustomListItems przy użyciu zmiennych i obiektów zdefiniowanych w rozszerzeniu. Operatory logiczne, operatory arytmetyczne i operatory przypisania można używać z Exec. Nie można użyć Exec do oceny funkcji, z wyjątkiem funkcji wewnętrznych debugera obsługiwanych przez ewaluatora wyrażeń języka C++.
Poniższa wizualizacja CAtlMap jest doskonałym przykładem, gdzie CustomListItems jest 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 przy użyciu 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, RightPointeri ValueNode są oceniane w kontekście klasy węzła drzewa.
ValueNode można pozostawić puste lub użyć this, aby odwołać się do samego węzła TreeItems.
Rozszerzenie funkcji 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 dziećmi 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 inteligentny typ wskaźnika auto_ptr<vector<int>> zazwyczaj jest wyświetlany jako:
Aby wyświetlić wartości wektora, należy zagłębić się dwa poziomy w oknie zmiennej, przechodząc przez człon _Myptr. Dodając element ExpandedItem, można wyeliminować zmienną _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, że klasa CPanel pochodzi z CFrameworkElement. Zamiast powtarzać właściwości pochodzące z klasy podstawowej CFrameworkElement, wizualizacja węzła ExpandedItem dołącza te właściwości do listy podrzędnej klasy CPanel.
<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 , który wyłącza dopasowywanie wizualizacji dla klasy pochodnej, jest konieczne tutaj. W przeciwnym razie wyrażenie *(CFrameworkElement*)this spowodowałoby ponowne zastosowanie wizualizacji CPanel, ponieważ domyślne reguły dopasowywania typów wizualizacji uznają ją za najbardziej odpowiednią. Użyj specyfikatora formatu , aby poinstruować debugera o użyciu wizualizacji klasy bazowej lub domyślne rozszerzenie, jeśli klasa bazowa nie ma wizualizacji.
Rozszerzenie elementu syntetycznego
Chociaż element ExpandedItem zapewnia bardziej pochlebny widok danych poprzez wyeliminowanie hierarchii, węzeł Synthetic wykonuje odwrotnie. 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 węzła Synthetic, aby wyświetlić użytkownikowi komunikat diagnostyczny:
<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 wewnętrzne
Niestandardowa funkcja wewnętrzna, którą można wywołać z wyrażenia. Elementowi <Intrinsic> musi towarzyszyć składnik debugera, który implementuje funkcję za pośrednictwem interfejsu IDkmIntrinsicFunctionEvaluator140. Aby uzyskać więcej informacji na temat implementacji niestandardowej funkcji wewnętrznej, zobacz Implementacja niestandardowej funkcji wewnętrznej NatVis.
<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 HRESULT w oknach debuggera. 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>
Element UIVisualizer
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 VSPackagei musi udostępnić 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 oraz typy, które może wizualizować.
Oto przykład elementu wizualizatora interfejsu użytkownika:
<?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 atrybutów
ServiceId-IdidentyfikujeUIVisualizer.ServiceIdto identyfikator GUID usługi, którą udostępnia pakiet wizualizatora.Idjest 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
MenuNamedefiniuje nazwę wizualizatora do wyświetlenia na liście rozwijanej obok ikony lupy w debugerze. Na przykład:menu skrótów Visualizera interfejsu użytkownika

Każdy typ zdefiniowany w pliku .natvis musi jawnie wymienić wszystkie wizualizatory interfejsu użytkownika, które mogą go wyświetlić. Debugger dopasowuje odwołania wizualizatora we wpisach typu do zarejestrowanych wizualizatorów. Na przykład następujący wpis typu dla std::vector odwołuje się do UIVisualizer w poprzednim przykładzie.
<Type Name="std::vector<int,*>">
<UIVisualizer ServiceId="{5452AFEA-3DF6-46BB-9177-C0B08F318025}" Id="1" />
</Type>
Przykład UIVisualizer można zobaczyć w rozszerzeniu Image Watch używanym do wyświetlania map bitowych w pamięci.
element CustomVisualizer
CustomVisualizer to punkt rozszerzalności określający rozszerzenie VSIX, które zapisujesz w celu sterowania wizualizacjami w programie Visual Studio Code. Aby uzyskać więcej informacji na temat pisania rozszerzeń VSIX, zobacz zestaw SDK Visual Studio.
Pisanie niestandardowego wizualizatora jest o wiele bardziej pracochłonne niż tworzenie definicji XML Natvis, ale nie ma ograniczeń dotyczących tego, co Natvis obsługuje 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żna użyć atrybutów Condition, IncludeViewi ExcludeView dla elementów CustomVisualizer.
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 watch debugera, takim jak wyrażenie mydoublearray, [100], które pokazuje pierwsze 100 elementów.