Bagikan melalui


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 Lokal dan jendela Watch, dan di DataTips. Visualisasi Natvis dapat membantu membuat jenis yang Anda buat lebih tampak jelas selama penelusuran kesalahan.

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

Nota

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

Visualisasi Natvis

Anda menggunakan framework Natvis untuk membuat aturan visualisasi untuk jenis yang Anda buat sehingga dapat lebih mudah dilihat oleh pengembang selama proses debugging.

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

Tampilan default TextBox

Baris yang disorot menunjukkan properti kelas TextTextBox. 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 kotak teks.

TextBox yang sama terlihat jauh lebih sederhana di jendela variabel saat 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 didefinisikan 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.

<?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 simpul proyek C++ di Penjelajah Solusi, dan pilih Project>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 Solution Explorer, 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 yang dibangun, 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 bawaan.

Untuk mengecualikan file .natvis dari .pdb:

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

  2. Klik tanda panah di sebelah Dikecualikan Dari Build dan pilih Ya, kemudian pilih OK.

Nota

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.

Nota

Aturan Natvis yang dimuat dari .pdb hanya berlaku untuk jenis-jenis dalam modul yang dirujuk oleh .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 mendefinisikan kelas bernama Test, entri Module1.pdb Natvis tidak berlaku untuknya.

Untuk menginstal dan mendaftarkan file .natvis melalui paket VSIX:

Paket VSIX dapat menginstal dan mendaftarkan file .natvis. Di mana pun file .natvis terdaftar dipasang, mereka secara otomatis terdeteksi dan digunakan selama proses debugging.

  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 dalam 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 Anda 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 sedang Anda debug, kecuali file dengan nama yang sama ada di dalam proyek yang sedang dimuat.

  2. Setiap file .natvis yang ada dalam proyek C++ yang sudah 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 diinstal 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 (<Folder Penginstalan Microsoft Visual Studio>\Common7\Packages\Debugger\Visualizers). Direktori ini memiliki file .natvis yang diinstal dengan Visual Studio. Jika Anda memiliki izin administrator, Anda bisa menambahkan file ke direktori ini.

Memodifikasi file .natvis saat debugging

Anda dapat memodifikasi file .natvis di IDE saat men-debug proyeknya. Buka file dalam instans Visual Studio yang sama dengan yang Anda debug, ubah, dan simpan. Segera setelah file disimpan, jendela Watch dan Locals diupdate untuk mencerminkan perubahan tersebut.

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

Anda tidak dapat memperbarui file .natvis yang disematkan dalam file .pdb saat Anda melakukan debugging.

Jika Anda memodifikasi file .natvis di luar Visual Studio, perubahan tidak berlaku secara otomatis. Untuk memperbarui jendela debugger, Anda dapat mengevaluasi kembali perintah .natvisreload di jendela langsung. Perubahan berlaku tanpa memulai ulang sesi debugging.

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-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 sedang 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 dapat menggunakan salah satu penentu format yang dijelaskan dalam penentu Format di C++. Penentu format diabaikan ketika entri digunakan secara internal oleh Natvis, seperti ekspresi Size dalam ekspansi ArrayItems .

Nota

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. Misalnya:
\<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, cuplikan berikut menunjukkan 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 tentukan tampilan alternatif. Tampilan sederhana muncul sebagai vec,view(simple):

jendela Pemantauan dengan tampilan sederhana

Kesalahan Natvis

Ketika debugger mengalami kesalahan dalam entri visualisasi, debugger akan mengabaikannya. Ini menampilkan tipe dalam bentuk aslinya, atau memilih visualisasi lain yang sesuai. Anda dapat menggunakan diagnostik Natvis untuk memahami mengapa debugger mengabaikan entri visualisasi, dan untuk melihat sintaks dan kesalahan penguraian yang mendasar.

Untuk mengaktifkan diagnostik Natvis:

  1. Buka panelOpsi> dan perluas bagianUmum>>. Tindakan Debug>Opsi membuka panel pada bagian yang sama.

  2. Di bawah Jendela Output>Pengaturan Output Umum, atur opsi Pesan diagnostik Natvis (hanya C++) ke Kesalahan, Peringatan, atau Verbositas.

  1. Buka dialogOpsi> dan perluas bagianUmum>. Tindakan Debug>Opsi membuka dialog ke bagian yang sama.

  2. Di bawah Jendela>Pengaturan Output Umum, atur opsi Pesan diagnostik Natvis (hanya C++) ke Kesalahan, Peringatan, atau Verbose (Detil).

  3. Pilih OK.

Kesalahan muncul di jendela Output.

Referensi sintaks Natvis

Elemen dan atribut berikut dapat digunakan dalam file Natvis.

Elemen AutoVisualizer

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

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

Elemen AutoVisualizer dapat memiliki Type, HResult, UIVisualizer, dan CustomVisualizer children.

Ketik elemen

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 (atribut Name).

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

  3. Seperti apa anggota jenis tersebut ketika pengguna memperluas jenis di jendela variabel (simpul Expand).

Kelas bertemplate

Atribut Name dari elemen Type menerima tanda bintang * sebagai karakter pengganti yang dapat digunakan untuk nama kelas yang diteplati.

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 yang umum.

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

Anda dapat mereferensikan parameter templat dalam entri visualisasi dengan menggunakan makro $T 1, $T 2, dan sebagainya. 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 Inheritable opsional 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 Priority opsional menentukan urutan penggunaan definisi alternatif, jika definisi gagal diurai. Nilai yang mungkin 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 terlebih dahulu mengurai entri yang cocok dengan STL 2015. Jika parsing gagal, 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 menempatkan atribut Optional pada simpul apa pun. Jika subekspresi di dalam simpul opsional gagal diurai, debugger mengabaikan simpul tersebut, tetapi menerapkan aturan Type lainnya. Dalam jenis berikut, [State] bersifat non-opsional, tetapi [Exception] bersifat opsional. Jika MyNamespace::MyClass memiliki bidang bernama _M_exceptionHolder, simpul [State] dan simpul [Exception] muncul, tetapi jika tidak ada bidang _M_exceptionHolder, hanya simpul [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 Condition opsional tersedia untuk banyak elemen visualisasi, dan menentukan kapan menggunakan aturan visualisasi. Jika ekspresi di dalam atribut kondisi diselesaikan ke false, aturan visualisasi tidak berlaku. Jika dievaluasi ke true, atau Condition tidak memiliki atribut, 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 DisplayString pertama dianggap sebagai true, sehingga formulir tersebut ditampilkan. Saat _Myptr anggota tidak kosong, kondisi dievaluasi menjadi false, dan elemen kedua DisplayString akan 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>

Sertakan atribut IncludeView dan ExcludeView

Atribut IncludeView dan ExcludeView menentukan elemen yang akan ditampilkan atau tidak ditampilkan dalam tampilan tertentu. Misalnya, dalam spesifikasi Natvis berikut dari std::vector, 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 membatasi entri visualisasi pada 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 ketika jenis 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 hingga 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 memerlukan Min dan Max. Mereka adalah atribut opsional. Tidak ada karakter wildcard yang didukung.

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

Elemen DisplayString

Elemen DisplayString menentukan string untuk ditampilkan sebagai nilai variabel. Ini menerima string sembarang yang dicampur dengan ekspresi. Segala sesuatu di dalam kurung kurawal ditafsirkan sebagai ekspresi. Misalnya, entri DisplayString berikut:

<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 }} ).

Nota

Elemen DisplayString adalah satu-satunya elemen yang dapat menerima string sembarang dan menggunakan sintaks dengan 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, dengan 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:

CStringT DisplayString elemen

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

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

Selama debugging, Anda dapat memilih ikon kaca pembesar di samping variabel, lalu memilih Text Visualizer untuk menampilkan string yang ditunjuk oleh m_pszData.

data CStringT dengan visualizer StringView

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

Perluas elemen

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

  • Jika simpul Expand tidak ditentukan dalam entri visualisasi, anak-anak menggunakan aturan ekspansi default.

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

Perluasan barang

Elemen Item adalah elemen paling dasar dan umum dalam simpul Expand. Item mendefinisikan elemen anak satu-satunya. 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, jenis CRect terlihat seperti contoh ini:

CRect dengan ekspansi elemen Item

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

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

Nota

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

Ekspansi ArrayItems

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

Simpul ArrayItems harus memiliki:

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

Nilai default batas bawah array adalah 0. Untuk mengambil alih nilai, gunakan elemen LowerBound. File .natvis yang disertakan dengan Visual Studio memiliki contoh.

Nota

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

Anda juga dapat menentukan array multi-dimensi. Dalam hal ini, debugger membutuhkan sedikit lebih banyak informasi untuk menampilkan elemen anak 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 baris-utama atau kolom-utama.
  • Rank menentukan peringkat array.
  • Elemen Size menerima parameter $i implisit, 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 $i implisit. Parameter $i diganti dengan indeks dimensi untuk menemukan batas bawah array dalam dimensi tersebut.
    • Dalam contoh sebelumnya, semua dimensi dimulai pada 0. Namun, jika Anda memiliki ($i == 1) ? 1000 : 100 batas bawah, dimensi ke-0 dimulai pada 100, dan dimensi pertama dimulai pada 1000.
      • , seperti [100, 1000], [100, 1001], [100, 1002], ... [101, 1000], [101, 1001],...

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

Array dua dimensi dengan ekspansi ArrayItems

Ekspansi IndexListItems

Anda dapat menggunakan ekspansi ArrayItems hanya jika elemen array ditata secara berdampingan dalam memori. Debugger mencapai elemen berikutnya hanya dengan menambah nilai penunjuknya. Jika Anda perlu memanipulasi indeks ke simpul nilai, gunakan simpul IndexListItems. Berikut adalah visualisasi dengan simpul 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 $i implisit.

Nota

Anda dapat menggunakan operator [], misalnya vector[i], dengan visualisasi array satu dimensi apa pun yang menggunakan IndexListItems, bahkan jika tipe tersebut sendiri (misalnya CATLArray) tidak mengizinkan operator ini.

Perluasan LinkedListItems

Jika jenis yang divisualisasikan mewakili daftar tertaut, debugger dapat menampilkan anak simpulnya dengan menggunakan simpul 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 simpul LinkedListItems, bukan jenis daftar induk. Dalam contoh sebelumnya, CAtlList memiliki kelas CNode (ditemukan di atlcoll.h) yang merupakan node dari daftar tertaut. m_pNext dan m_element adalah bidang dari kelas CNode tersebut, bukan dari kelas CAtlList.

ValueNode dapat dibiarkan kosong, atau menggunakan this untuk merujuk ke simpul LinkedListItems itu sendiri.

Pengembangan "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 sepenuhnya cocok dengan format ArrayItems, IndexListItems, atau LinkedListItems.

Anda dapat menggunakan Exec untuk menjalankan kode di dalam ekspansi CustomListItems, menggunakan variabel dan objek yang ditentukan dalam ekspansi. 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 didukung oleh evaluator ekspresi C++.

Visualizer berikut untuk CAtlMap adalah contoh yang sangat baik di mana 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>

Ekspansi TreeItems

Jika jenis yang divisualisasikan mewakili pohon, debugger dapat menelusuri pohon dan menampilkan turunannya dengan menggunakan simpul TreeItems sebagai referensi. Berikut adalah visualisasi untuk jenis std::map menggunakan simpul 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 simpul LinkedListItems. LeftPointer, RightPointer, dan ValueNode dievaluasi di bawah konteks kelas simpul pohon. ValueNode dapat dibiarkan kosong atau menggunakan this untuk merujuk ke simpul TreeItems itu sendiri.

Perluasan ExpandedItem

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

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

auto_ptr<vektor<int>> ekspansi default

Untuk melihat nilai vektor, Anda harus menelusuri 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<vektor<ekspansi int>> ExpandedItem ekspansi ExpandedItem

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 CFrameworkElement dasar, visualisasi simpul 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 dan untuk menginstruksikan debugger agar menggunakan visualisasi kelas dasar, atau ekspansi default jika kelas dasar tidak memiliki visualisasi.

Pengembangan item sintetis

Meskipun elemen ExpandedItem memberikan tampilan data yang lebih datar dengan menghilangkan hierarki, simpul Synthetic melakukan sebaliknya. Ini memungkinkan Anda untuk membuat elemen anak buatan yang bukan hasil dari ekspresi. Elemen buatan dapat memiliki elemen anak sendiri. Dalam contoh berikut, visualisasi untuk jenis Concurrency::array menggunakan simpul Synthetic untuk menampilkan pesan diagnostik kepada 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 intrinsik

Fungsi intrinsik kustom yang dapat dipanggil dari ekspresi. Elemen <Intrinsic> harus disertai dengan komponen debugger yang mengimplementasikan fungsi melalui antarmuka IDkmIntrinsicFunctionEvaluator140. Untuk informasi selengkapnya tentang menerapkan fungsi intrinsik kustom, lihat Menerapkan fungsi intrinsik kustom NatVis.

<Type Name="std::vector&lt;*&gt;">
  <Intrinsic Name="size" Expression="(size_t)(_Mypair._Myval2._Mylast - _Mypair._Myval2._Myfirst)" />
  <Intrinsic Name="capacity" Expression="(size_t)(_Mypair._Myval2._Myend - _Mypair._Myval2._Myfirst)" />
  <DisplayString>{{ size={size()} }}</DisplayString>
  <Expand>
    <Item Name="[capacity]" ExcludeView="simple">capacity()</Item>
    <Item Name="[allocator]" ExcludeView="simple">_Mypair</Item>
    <ArrayItems>
      <Size>size()</Size>
      <ValuePointer>_Mypair._Myval2._Myfirst</ValuePointer>
    </ArrayItems>
  </Expand>
</Type>

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 hendak 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 pada 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 dibuat sebagai VSPackage, dan harus mengekspos layanan yang dapat digunakan 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 atribut ServiceId - Id mengidentifikasi UIVisualizer. ServiceId adalah GUID dari layanan yang diekspos oleh paket pemvisualisasi. 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. Misalnya:

    menu pintasan menu UIVisualizer

Setiap jenis yang ditentukan dalam file .natvis harus secara eksplisit mencantumkan visualizer UI apa pun yang dapat menampilkannya. Debugger menyamakan referensi visualizer dalam entri tipe dengan visualizer yang 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 pada 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.

Ini 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 kumpulan 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.

Keterbatasan

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

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