Membuat tampilan kustom objek C++ di debugger menggunakan kerangka kerja Natvis

Kerangka kerja Visual Studio Natvis menyesuaikan cara jenis asli muncul di jendela variabel debugger, seperti jendela Locals dan Watch, dan di DataTips. Visualisasi Natvis dapat membantu membuat jenis yang Anda buat lebih terlihat selama penelusuran kesalahan.

Natvis menggantikan file autoexp.dat dalam versi Visual Studio sebelumnya dengan sintaks XML, diagnostik, penerapan versi, dan beberapa dukungan file yang lebih baik.

Catatan

Kustomisasi Natvis berfungsi dengan kelas dan struktur, tetapi tidak dengan typedefs.

Visualisasi Natvis

Anda menggunakan kerangka kerja Natvis untuk membuat aturan visualisasi untuk jenis yang Anda buat, sehingga pengembang dapat melihatnya dengan lebih mudah selama penelusuran kesalahan.

Misalnya, ilustrasi berikut menunjukkan variabel jenis Windows::UI::XAML::Controls::TextBox di jendela debugger tanpa visualisasi kustom yang diterapkan.

Visualisasi default TextBox

Baris yang disorot memperlihatkan properti Text kelas TextBox. Hierarki kelas yang kompleks menyulitkan untuk menemukan properti ini. Debugger tidak tahu cara menginterpretasikan jenis string kustom, sehingga Anda tidak dapat melihat string yang disimpan di dalam kotakteks.

TextBox yang sama terlihat jauh lebih sederhana di jendela variabel ketika aturan visualizer kustom Natvis diterapkan. Anggota penting kelas muncul bersama-sama, dan debugger menunjukkan nilai string yang mendasar dari jenis string kustom.

Data TextBox menggunakan visualizer

Menggunakan file .natvis dalam proyek C++

Natvis menggunakan file .natvis untuk menentukan aturan visualisasi. File .natvis adalah file XML dengan ekstensi .natvis. Skema Natvis ditentukan dalam <Folder Penginstalan VS>\Xml\Schemas\1033\natvis.xsd.

Struktur dasar file .natvis adalah satu atau beberapa elemen Type yang mewakili entri visualisasi. Nama yang sepenuhnya memenuhi syarat dari setiap elemen Type ditentukan dalam atribut Name-nya.

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

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

Visual Studio menyediakan beberapa file .natvis di folder <Folder Penginstalan VS>\Common7\Packages\Debugger\Visualizers. File-file ini memiliki aturan visualisasi untuk banyak jenis umum, dan dapat berfungsi sebagai contoh untuk menulis visualisasi untuk jenis baru.

Menambahkan file .natvis ke proyek C++

Anda dapat menambahkan file .natvis ke proyek C++ apa pun.

Untuk menambahkan file .natvis baru:

  1. Pilih node proyek C++ di Penjelajah Solusi, dan pilih Proyek>Tambahkan item baru, atau klik kanan proyek dan pilih Tambahkan>Item baru.

    Jika Anda tidak melihat semua templat item, pilih Perlihatkan Semua Templat.

  2. Dalam dialog Tambahkan Item Baru, pilih Visual C++>Utilitas>file visualisasi Debugger (.natvis).

  3. Beri nama file, dan pilih Tambahkan.

    File baru ditambahkan ke Penjelajah Solusi, dan terbuka di panel dokumen Visual Studio.

Debugger Visual Studio memuat file .natvis dalam proyek C++ secara otomatis, dan secara default, juga menyertakannya dalam file .pdb saat proyek dibangun. Jika Anda men-debug aplikasi bawaan, debugger memuat file .natvis dari file .pdb, bahkan jika Anda tidak membuka proyek. Jika Anda tidak ingin file .natvis disertakan dalam .pdb, Anda dapat mengecualikannya dari file .pdb yang dibangun.

Untuk mengecualikan file .natvis dari .pdb:

  1. Pilih file .natvis di Penjelajah Solusi, dan pilih ikon Properti, atau klik kanan file dan pilih Properti.

  2. Daftar dropdown panah di samping Dikecualikan Dari Build dan pilih Ya, lalu pilih OK.

Catatan

Untuk men-debug proyek yang dapat dieksekusi, gunakan item solusi untuk menambahkan file .natvis apa pun yang tidak ada di .pdb, karena tidak ada proyek C++ yang tersedia.

Catatan

Aturan Natvis yang dimuat dari .pdb hanya berlaku untuk jenis dalam modul yang dirujuk .pdb. Misalnya, jika Module1.pdb memiliki entri Natvis untuk jenis bernama Test, itu hanya berlaku untuk kelas Test di Module1.dll. Jika modul lain juga menentukan kelas bernama Test, entri Natvis Module1.pdb tidak berlaku untuknya.

Untuk memasang dan mendaftarkan file .natvis melalui paket VSIX:

Paket VSIX dapat memasang dan mendaftarkan file .natvis. Di mana pun mereka diinstal, semua file .natvis terdaftar secara otomatis diambil selama penelusuran kesalahan.

  1. Sertakan file .natvis dalam paket VSIX. Misalnya, untuk file proyek berikut:

    <?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. Daftarkan file .natvis di file 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>
    

Lokasi file Natvis

Anda dapat menambahkan file .natvis ke direktori pengguna atau ke direktori sistem, jika Anda ingin file tersebut diterapkan ke beberapa proyek.

File .natvis dievaluasi dalam urutan berikut:

  1. Setiap file .natvis yang disematkan dalam .pdb yang Anda telusuri kesalahannya, kecuali file dengan nama yang sama ada dalam proyek yang dimuat.

  2. File .natvis apa pun yang berada dalam proyek C++ yang dimuat atau solusi tingkat atas. Grup ini mencakup semua proyek C++ yang dimuat, termasuk pustaka kelas, tetapi bukan proyek dalam bahasa lain.

  3. File .natvis apa pun yang dipasang dan didaftarkan melalui paket VSIX.

  1. Direktori Natvis khusus pengguna (misalnya, %USERPROFILE%\Documents\Visual Studio 2022\Visualizers).
  1. Direktori Natvis khusus pengguna (misalnya, %USERPROFILE%\Documents\Visual Studio 2019\Visualizers).
  1. Direktori Natvis di seluruh sistem (<Microsoft Visual Studio Installation Folder>\Common7\Packages\Debugger\Visualizers). Direktori ini memiliki file .natvis yang dipasang dengan Visual Studio. Jika Anda memiliki izin administrator, Anda bisa menambahkan file ke direktori ini.

Memodifikasi file .natvis saat menelusuri kesalahan

Anda dapat memodifikasi file .natvis di IDE saat menelusuri kesalahan proyeknya. Buka file dalam instans Visual Studio yang sama yang Anda gunakan untuk menelusuri kesalahan, modifikasi, dan simpan. Segera setelah file disimpan, jendela Watch dan Locals memperbarui untuk mencerminkan perubahan.

Anda juga dapat menambahkan atau menghapus file .natvis dalam solusi yang Anda telusuri kesalahan, dan Visual Studio menambahkan atau menghapus visualisasi yang relevan.

Anda tidak dapat memperbarui file .natvis yang disematkan dalam file .pdb saat Anda menelusuri kesalahan.

Jika Anda mengubah file .natvis di luar Visual Studio, perubahan tidak berlaku secara otomatis. Untuk memperbarui jendela debugger, Anda dapat mengevaluasi kembali perintah .natvisreload di jendela Immediate. Kemudian perubahan berlaku tanpa memulai ulang sesi penelusuran kesalahan.

Gunakan juga perintah .natvisreload untuk meningkatkan file .natvis ke versi yang lebih baru. Misalnya, file .natvis mungkin diperiksa ke kontrol sumber, dan Anda ingin mengambil perubahan terbaru yang dibuat orang lain.

Ekspresi dan pemformatan

Visualisasi Natvis menggunakan ekspresi C++ untuk menentukan item data yang akan ditampilkan. Selain peningkatan dan batasan ekspresi C++ dalam debugger, yang dijelaskan dalam Operator konteks (C++), perhatikan hal berikut:

  • Ekspresi Natvis dievaluasi dalam konteks objek yang divisualisasikan, bukan bingkai tumpukan saat ini. Misalnya, x dalam ekspresi Natvis mengacu pada bidang bernama x dalam objek yang divisualisasikan, bukan ke variabel lokal bernama x dalam fungsi saat ini. Anda tidak dapat mengakses variabel lokal dalam ekspresi Natvis, meskipun Anda dapat mengakses variabel global.

  • Ekspresi Natvis tidak mengizinkan evaluasi fungsi atau efek samping. Panggilan fungsi dan operator penugasan diabaikan. Karena fungsi intrinsik debugger bebas efek samping, fungsi tersebut mungkin dipanggil dengan bebas dari ekspresi Natvis apa pun, meskipun panggilan fungsi lain tidak diizinkan.

  • Untuk mengontrol bagaimana ekspresi ditampilkan, Anda bisa menggunakan salah satu penentu format yang dijelaskan dalam Penentu format di C++. Penentu format diabaikan ketika entri digunakan secara internal oleh Natvis, seperti Size ekspresi dalam ekspansi ArrayItems.

Catatan

Karena dokumen Natvis adalah XML, ekspresi Anda tidak dapat langsung menggunakan operator ampersand, lebih besar dari, kurang dari, atau shift. Anda harus keluar dari karakter ini dalam isi item dan pernyataan kondisi. Contohnya:
\<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\>

Tampilan Natvis

Anda dapat menentukan tampilan Natvis yang berbeda untuk menampilkan jenis dengan cara yang berbeda. Misalnya, berikut adalah visualisasi std::vector yang menentukan tampilan yang disederhanakan bernama simple. Elemen DisplayString dan ArrayItems ditampilkan dalam tampilan default dan tampilan simple, sementara item [size] dan [capacity] tidak ditampilkan dalam tampilan simple.

<Type Name="std::vector&lt;*&gt;">
    <DisplayString>{{ size={_Mylast - _Myfirst} }}</DisplayString>
    <Expand>
        <Item Name="[size]" ExcludeView="simple">_Mylast - _Myfirst</Item>
        <Item Name="[capacity]" ExcludeView="simple">_Myend - _Myfirst</Item>
        <ArrayItems>
            <Size>_Mylast - _Myfirst</Size>
            <ValuePointer>_Myfirst</ValuePointer>
        </ArrayItems>
    </Expand>
</Type>

Di jendela Watch, gunakan penentu format ,lihat untuk menentukan tampilan alternatif. Tampilan sederhana muncul sebagai vec,view(simple):

Jendela pengawas dengan tampilan sederhana

Kesalahan Natvis

Saat debugger mengalami kesalahan dalam entri visualisasi, debugger akan mengabaikannya. Debugger menampilkan jenis dalam bentuk mentahnya, atau memilih visualisasi lain yang sesuai. Anda dapat menggunakan diagnostik Natvis untuk memahami mengapa debugger mengabaikan entri visualisasi, dan untuk melihat sintaks yang mendasar dan mengurai kesalahan.

Untuk mengaktifkan diagnostik Natvis:

  • Di bawah Alat>Opsi (atau Debug>Opsi) >Penelusuran Kesalahan>Jendela Output, atur Pesan diagnostik Natvis (hanya C++) ke Kesalahan, Peringatan, atau Verbose, kemudian pilih OK.

Kesalahan muncul di jendela Output.

Referensi sintaks Natvis

Elemen dan atribut berikut dapat digunakan dalam file Natvis.

Elemen AutoVisualizer

Elemen AutoVisualizer adalah node akar dari file .natvis, dan berisi atribut namespace xmlns:.

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

Elemen AutoVisualizer dapat memiliki turunan Jenis, HResult, UIVisualizer, dan CustomVisualizer.

Elemen jenis

Type dasar terlihat seperti contoh ini:

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

Elemen Type menentukan:

  1. Jenis visualisasi apa yang harus digunakan untuk (Name atribut).

  2. Seperti apa harusnya nilai objek dari jenis tersebut terlihat (elemen DisplayString).

  3. Seperti apa harusnya anggota jenis tersebut terlihat saat pengguna memperluas jenis di jendela variabel (node Expand).

Kelas yang di-templat

Atribut Name elemen Type menerima tanda bintang * sebagai wildcard yang dapat digunakan untuk nama kelas templat.

Dalam contoh berikut, visualisasi yang sama digunakan apakah objek adalah CAtlArray<int> atau CAtlArray<float>. Jika ada entri visualisasi tertentu untuk CAtlArray<float>, maka itu lebih diutamakan daripada entri generik.

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

Anda dapat mereferensikan parameter templat dalam entri visualisasi dengan menggunakan makro $T1, $T2, dan seterusnya. Untuk menemukan contoh makro ini, lihat file .natvis yang dikirim dengan Visual Studio.

Pencocokan jenis visualizer

Jika entri visualisasi gagal divalidasi, visualisasi berikutnya yang tersedia akan digunakan.

Atribut yang dapat diwariskan

Atribut opsional Inheritable menentukan apakah visualisasi hanya berlaku untuk jenis dasar, atau ke jenis dasar dan semua jenis turunan. Nilai default Inheritable adalah true.

Dalam contoh berikut, visualisasi hanya berlaku untuk jenis BaseClass:

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

Atribut prioritas

Atribut opsional Priority menentukan urutan untuk menggunakan definisi alternatif, jika definisi gagal diurai. Nilai yang memungkinkan dari Priority adalah: Low, MediumLow,Medium, MediumHigh, dan High. Nilai defaultnya adalah Medium. Atribut Priority hanya membedakan di antara prioritas dalam file .natvis yang sama.

Contoh berikut pertama kali mengurai entri yang cocok dengan STL 2015. Jika gagal diurai, entri alternatif akan digunakan untuk STL versi 2013:

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

Atribut opsional

Anda dapat meletakkan atribut Optional pada node apa pun. Jika subekspresi di dalam node opsional gagal diurai, debugger mengabaikan node tersebut, tetapi menerapkan aturan lainnya Type. Dalam jenis berikut, [State] tidak opsional, tetapi [Exception] bersifat opsional. Jika MyNamespace::MyClass memiliki bidang bernama _M_exceptionHolder, node [State] dan node [Exception] muncul, tetapi jika _M_exceptionHolder tidak ada bidang, hanya node [State] yang muncul.

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

Atribut kondisi

Atribut opsional Condition tersedia untuk banyak elemen visualisasi, dan menentukan kapan menggunakan aturan visualisasi. Jika ekspresi dalam atribut kondisi diselesaikan ke false, aturan visualisasi tidak berlaku. Jika mengevaluasi ke true, atau tidak ada atributCondition, visualisasi berlaku. Anda dapat menggunakan atribut ini untuk logika if-else dalam entri visualisasi.

Misalnya, visualisasi berikut memiliki dua elemen DisplayString untuk jenis penunjuk cerdas. Ketika anggota _Myptr kosong, kondisi elemen pertama DisplayString diselesaikan ke true, sehingga formulir ditampilkan. Ketika anggota _Myptr tidak kosong, kondisi dievaluasi ke false, dan elemen kedua DisplayString ditampilkan.

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

Atribut IncludeView dan ExcludeView

Atribut IncludeView dan ExcludeView menentukan elemen untuk ditampilkan atau tidak ditampilkan dalam tampilan tertentu. Misalnya, dalam spesifikasi Natvis std::vector berikut, tampilan simple tidak menampilkan item [size] dan [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>

Anda dapat menggunakan atribut IncludeView dan ExcludeView pada jenis dan pada masing-masing anggota.

Elemen versi

Elemen Version mencakup entri visualisasi ke modul dan versi tertentu. Elemen Version membantu menghindari tabrakan nama, mengurangi ketidakcocokan yang tidak disengaja, dan memungkinkan visualisasi yang berbeda untuk versi jenis yang berbeda.

Jika file header umum yang digunakan oleh modul yang berbeda menentukan jenis, visualisasi versi hanya muncul saat jenisnya berada dalam versi modul yang ditentukan.

Dalam contoh berikut, visualisasi hanya berlaku untuk jenis DirectUI::Border yang ditemukan di Windows.UI.Xaml.dll dari versi 1.0 ke 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>

Anda tidak membutuhkan Min dan Max. Mereka adalah atribut opsional. Tidak ada wildcard yang didukung.

Atribut Name dalam format filename.ext, seperti hello.exe atau some.dll. Tidak ada nama jalur yang diperbolehkan.

Elemen DisplayString

Elemen DisplayString menentukan string untuk ditampilkan sebagai nilai variabel. Elemen tersebut menerima string arbitrer yang dicampur dengan ekspresi. Segala yang ada di dalam kurung kurawal diinterpretasikan sebagai ekspresi. Misalnya, entri berikut DisplayString:

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

Berarti bahwa variabel jenis CPoint ditampilkan seperti dalam ilustrasi ini:

Menggunakan elemen DisplayString

Dalam ekspresi DisplayString, x dan y, yang merupakan anggota CPoint, berada di dalam kurung kurawal, sehingga nilainya dievaluasi. Contohnya juga menunjukkan bagaimana Anda dapat lolos dari kurung kurawal dengan menggunakan kurung kurawal ganda ( {{ atau }} ).

Catatan

Elemen DisplayString adalah satu-satunya elemen yang menerima string arbitrer dan sintaks kurung kurawal. Semua elemen visualisasi lainnya hanya menerima ekspresi yang dapat dievaluasi oleh debugger.

Elemen StringView

Elemen StringView menentukan nilai yang dapat dikirim debugger ke visualizer teks bawaan. Misalnya, mengingat visualisasi berikut untuk jenis ATL::CStringT:

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

Objek CStringT ditampilkan di jendela variabel seperti contoh ini:

Elemen CStringT DisplayString

Menambahkan elemen StringView memberi tahu debugger bahwa nilai dapat ditampilkan sebagai visualisasi teks.

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

Selama penelusuran kesalahan, Anda dapat memilih ikon kaca pembesar di samping variabel, lalu pilih Visualizer Teks untuk menampilkan string yang ditunjuk m_pszData.

Data CStringT dengan visualizer StringView

Ekspresi {m_pszData,su} termasuk penentu format C++ su, untuk menampilkan nilai sebagai string Unicode. Untuk informasi selengkapnya, lihat Penentu format di C++.

Perluas elemen

Node opsional Expand menyesuaikan turunan dari jenis yang divisualisasikan saat Anda memperluas jenis di jendela variabel. Node Expand menerima daftar node anak yang menentukan elemen turunan.

  • Jika node Expand tidak ditentukan dalam entri visualisasi, turunan menggunakan aturan perluasan default.

  • Jika node Expand ditentukan tanpa node anak di bawahnya, jenisnya tidak dapat diperluas di jendela debugger.

Perluasan item

Elemen Item adalah elemen yang paling dasar dan umum dalam sebuah node Expand. Item menentukan elemen turunan tunggal. Misalnya, kelas CRect dengan bidang top, left, right, dan bottom memiliki entri visualisasi berikut:

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

Di jendela debugger, jenisnya CRect terlihat seperti contoh ini:

CRect dengan ekspansi elemen Item

Debugger mengevaluasi ekspresi yang ditentukan dalam elemen Width dan Height, dan memperlihatkan nilai di kolom Nilai dari jendela variabel.

Debugger secara otomatis membuat node [Tampilan Mentah] untuk setiap perluasan kustom. Cuplikan layar sebelumnya menampilkan node [Tampilan Mentah] yang diperluas, untuk menunjukkan bagaimana tampilan mentah default objek berbeda dari visualisasi Natvis-nya. Perluasan default membuat subtree untuk kelas dasar, dan mencantumkan semua anggota data kelas dasar sebagai turunan.

Catatan

Jika ekspresi elemen item menunjuk ke jenis yang kompleks, node Item itu sendiri dapat diperluas.

Perluasan ArrayItems

Gunakan node ArrayItems agar debugger Visual Studio menginterpretasikan jenis sebagai array dan menampilkan elemen individualnya. Visualisasi untuk std::vector adalah contoh yang baik:

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

std::vector menunjukkan elemen individualnya saat diperluas di jendela variabel:

std::vector menggunakan ekspansi ArrayItems

Node ArrayItems harus memiliki:

  • Ekspresi Size (yang harus mengevaluasi ke bilangan bulat) agar debugger memahami panjang array.
  • Ekspresi ValuePointer yang menunjuk ke elemen pertama (yang harus menjadi penunjuk dari jenis elemen yang bukan void*).

Nilai default array yang terikat lebih rendah adalah 0. Untuk mengambil alih nilai, gunakan elemen LowerBound. File .natvis yang dikirim dengan Visual Studio memiliki contoh.

Catatan

Anda dapat menggunakan operator [], misalnya vector[i], dengan visualisasi array dimensi tunggal yang menggunakan ArrayItems, bahkan jika jenis itu sendiri (misalnya CATLArray) tidak mengizinkan operator ini.

Anda juga dapat menentukan array multi-dimensi. Dalam hal ini, debugger membutuhkan sedikit lebih banyak informasi untuk menampilkan elemen turunan dengan benar:

<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 menentukan apakah array berada dalam urutan row-major atau column-major.
  • Rank menentukan peringkat array.
  • Elemen Size menerima parameter implisit $i, yang diganti dengan indeks dimensi untuk menemukan panjang array dalam dimensi tersebut.
    • Dalam contoh sebelumnya, ekspresi _M_extent.M_base[0] harus memberikan panjang dimensi ke-0, _M_extent._M_base[1] yang pertama, dan sebagainya.
  • LowerBound menentukan batas bawah setiap dimensi array. Untuk array multidimensi, Anda dapat menentukan ekspresi yang menggunakan parameter implisit $i . Parameter $i akan diganti dengan indeks dimensi untuk menemukan batas bawah array dalam dimensi tersebut.
    • Dalam contoh sebelumnya, semua dimensi akan dimulai pada 0. Namun, jika Anda memiliki ($i == 1) ? 1000 : 100 batas bawah, dimensi ke-0 akan dimulai pada 100, dan dimensi pertama akan dimulai pada 1000.
      • Seperti [100, 1000], [100, 1001], [100, 1002], ... [101, 1000], [101, 1001],...

Berikut adalah tampilan objek dua dimensi Concurrency::array di jendela debugger:

Array dua dimensi dengan ekspansi ArrayItems

Perluasan IndexListItems

Anda dapat menggunakan perluasan ArrayItems hanya jika elemen array ditata secara berdampingan dalam memori. Debugger masuk ke elemen berikutnya hanya dengan menambahkan pointer-nya. Jika Anda perlu memanipulasi indeks ke node nilai, gunakan node IndexListItems. Berikut adalah visualisasi dengan node 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>

Satu-satunya perbedaan antara ArrayItems dan IndexListItems adalah ValueNode, yang mengharapkan ekspresi lengkap ke elemen ith dengan parameter implisit $i.

Catatan

Anda dapat menggunakan operator [], misalnya vector[i], dengan visualisasi array dimensi tunggal yang menggunakan IndexListItems, bahkan jika jenis itu sendiri (misalnya CATLArray) tidak mengizinkan operator ini.

Perluasan LinkedListItems

Jika jenis yang divisualisasikan mewakili daftar tertaut, debugger dapat menampilkan turunannya dengan menggunakan node LinkedListItems. Visualisasi berikut untuk jenis CAtlList menggunakan 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>

Elemen Size mengacu pada panjang daftar. HeadPointer menunjuk ke elemen pertama, NextPointer mengacu pada elemen berikutnya, dan ValueNode mengacu pada nilai item.

Debugger mengevaluasi ekspresi NextPointer dan ValueNode dalam konteks elemen node LinkedListItems, bukan jenis daftar induk. Dalam contoh sebelumnya, CAtlList memiliki kelas CNode (ditemukan di atlcoll.h) yang merupakan node daftar tertaut. m_pNext dan m_element merupakan bidang dari kelas CNode tersebut, bukan dari kelas CAtlList.

ValueNode dapat dibiarkan kosong, atau gunakan this untuk merujuk ke node LinkedListItems itu sendiri.

Perluasan CustomListItems

Perluasan CustomListItems memungkinkan Anda menulis logika kustom untuk melintas struktur data seperti hashtable. Gunakan CustomListItems untuk memvisualisasikan struktur data yang dapat menggunakan ekspresi C++ untuk semua yang perlu Anda evaluasi, tetapi tidak cukup sesuai dengan cetakan untuk ArrayItems, IndexListItems, atau LinkedListItems.

Anda dapat menggunakan Exec untuk mengeksekusi kode di dalam perluasan CustomListItems, menggunakan variabel dan objek yang ditentukan dalam perluasan. Anda dapat menggunakan operator logis, operator aritmatika, dan operator penugasan dengan Exec. Anda tidak dapat menggunakan Exec untuk mengevaluasi fungsi, kecuali untuk fungsi intrinsik debugger yang didukung oleh evaluator ekspresi C++.

Visualizer berikut untuk CAtlMap adalah contoh yang sangat baik jika CustomListItems sesuai.

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

Perluasan TreeItems

Jika jenis yang divisualisasikan mewakili pohon, debugger dapat berjalan di pohon dan menampilkan turunannya dengan menggunakan node TreeItems. Berikut adalah visualisasi untuk jenis std::map menggunakan node 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>

Sintaksnya mirip dengan node LinkedListItems. LeftPointer, RightPointer, dan ValueNode dievaluasi di bawah konteks kelas node pohon. ValueNode dapat dibiarkan kosong atau gunakan this untuk merujuk ke node TreeItems itu sendiri.

Perluasan ExpandedItem

Elemen ExpandedItem menghasilkan tampilan turunan yang diagregasi dengan menampilkan properti kelas dasar atau anggota data seolah-olah mereka adalah turunan dari jenis yang divisualisasikan. Debugger mengevaluasi ekspresi yang ditentukan, dan menambahkan node anak dari hasil ke daftar turunan dari jenis yang divisualisasikan.

Misalnya, jenis penunjuk pintar auto_ptr<vector<int>> biasanya ditampilkan sebagai:

<auto_ptr ekspansi default vektor<int>>

Untuk melihat nilai vektor, Anda harus menelusuri paling detail dua tingkat di jendela variabel, melewati anggota _Myptr. Dengan menambahkan elemen ExpandedItem, Anda dapat menghilangkan variabel _Myptr dari hierarki dan langsung melihat elemen vektor:

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

<auto_ptr ekspansi ExpandedItem vektor<int>>

Contoh berikut menunjukkan cara mengagregasi properti dari kelas dasar di kelas turunan. Misalkan kelas CPanel berasal dari CFrameworkElement. Alih-alih mengulangi properti yang berasal dari kelas dasar CFrameworkElement, visualisasi node ExpandedItem menambahkan properti tersebut ke daftar turunan kelas CPanel.

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

Penentu format nd, yang menonaktifkan pencocokan visualisasi untuk kelas turunan, diperlukan di sini. Jika tidak, ekspresi *(CFrameworkElement*)this akan menyebabkan visualisasi CPanel diterapkan lagi, karena aturan pencocokan jenis visualisasi default menganggapnya yang paling tepat. Gunakan penentu format nd untuk menginstruksikan debugger untuk menggunakan visualisasi kelas dasar, atau perluasan default jika kelas dasar tidak memiliki visualisasi.

Perluasan item sintetis

Sementara elemen ExpandedItem menyediakan tampilan data yang lebih datar dengan menghilangkan hierarki, node Synthetic melakukan sebaliknya. Ini memungkinkan Anda untuk membuat elemen turunan buatan yang bukan hasil dari ekspresi. Elemen buatan dapat memiliki elemen turunan sendiri. Dalam contoh berikut, visualisasi untuk jenis Concurrency::array menggunakan node Synthetic untuk menampilkan pesan diagnostik ke pengguna:

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

Konkurensi::Array dengan ekspansi elemen Sintetis

Ekspansi instrinsik

Fungsi intrinsik kustom yang dapat dipanggil dari ekspresi. Elemen <Intrinsic> harus disertai dengan komponen debugger yang mengimplementasikan fungsi melalui antarmuka 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>

Elemen HResult

Elemen HResult memungkinkan Anda menyesuaikan informasi yang ditampilkan untuk HRESULT di jendela debugger. Elemen HRValue harus berisi nilai 32-bit dari HRESULT yang akan disesuaikan. Elemen HRDescription berisi informasi yang akan ditampilkan di jendela debugger.


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

Elemen UIVisualizer

Elemen UIVisualizer mendaftarkan plug-in visualizer grafis dengan debugger. Visualizer grafis membuat kotak dialog atau antarmuka lain yang memperlihatkan variabel atau objek dengan cara yang konsisten dengan jenis datanya. Plug-in visualizer harus ditulis sebagai VSPackage, dan harus mengekspos layanan yang dapat dikonsumsi debugger. File .natvis berisi informasi pendaftaran untuk plug-in, seperti namanya, pengidentifikasi unik global (GUID) dari layanan yang diekspos, dan jenis yang dapat divisualisasikannya.

Berikut adalah contoh elemen UIVisualizer:

<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
    <UIVisualizer ServiceId="{5452AFEA-3DF6-46BB-9177-C0B08F318025}"
        Id="1" MenuName="Vector Visualizer"/>
    <UIVisualizer ServiceId="{5452AFEA-3DF6-46BB-9177-C0B08F318025}"
        Id="2" MenuName="List Visualizer"/>
.
.
</AutoVisualizer>
  • Pasangan tribut ServiceId - Id attribute pair mengidentifikasi UIVisualizer. ServiceId adalah GUID layanan yang diekspos paket visualizer. Id adalah pengidentifikasi unik yang membedakan visualizer, jika layanan menyediakan lebih dari satu. Dalam contoh sebelumnya, layanan visualizer yang sama menyediakan dua visualizer.

  • Atribut MenuName menentukan nama visualizer untuk ditampilkan di daftar dropdown di samping ikon kaca pembesar di debugger. Contohnya:

    Menu pintasan menu UIVisualizer

Setiap jenis yang ditentukan dalam file .natvis harus secara eksplisit mencantumkan visualizer UI apa pun yang dapat menampilkannya. Debugger cocok dengan referensi visualizer dalam entri jenis dengan visualizer terdaftar. Misalnya, entri jenis berikut untuk std::vector mereferensikan UIVisualizer dalam contoh sebelumnya.

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

Anda dapat melihat contoh UIVisualizer di ekstensi Image Watch yang digunakan untuk melihat bitmap dalam memori.

Elemen CustomVisualizer

CustomVisualizer adalah titik ekstensibilitas yang menentukan ekstensi VSIX yang Anda tulis untuk mengontrol visualisasi di Visual Studio Code. Untuk informasi selengkapnya tentang menulis ekstensi VSIX, lihat Visual Studio SDK.

Akan jauh lebih banyak pekerjaan untuk menulis visualizer kustom daripada definisi XML Natvis, tetapi Anda bebas dari batasan tentang apa yang dilakukan atau tidak didukung Natvis. Visualizer kustom memiliki akses ke set lengkap API ekstensibilitas debugger, yang dapat mengkueri dan memodifikasi proses debuggee atau berkomunikasi dengan bagian lain dari Visual Studio.

Anda dapat menggunakan atribut Condition, IncludeView, dan ExcludeView pada elemen CustomVisualizer.

Batasan

Kustomisasi Natvis berfungsi dengan kelas dan struktur, tetapi tidak dengan typedefs.

Natvis tidak mendukung visualizer untuk jenis primitif (misalnya, int, bool) atau untuk penunjuk ke jenis primitif. Dalam skenario ini, salah satu opsinya adalah menggunakan penentu format yang sesuai dengan kasus penggunaan Anda. Misalnya, jika Anda menggunakan double* mydoublearray dalam kode, maka Anda dapat menggunakan penentu format array di jendela Watch debugger, seperti ekspresi mydoublearray, [100], yang menunjukkan 100 elemen pertama.