使用 Natvis 架構在偵錯工具中建立 C++ 物件的自訂檢視

Visual Studio Natvis 架構會自訂原生類型出現在偵錯工具變數視窗中的方式,例如 [區域變數] 和 [監看式] 視窗,以及 [資料提示]。 Natvis 視覺效果可協助您在偵錯期間建立更可見的類型。

Natvis 會將舊版 Visual Studio 中的 autoexp.dat 檔案取代為 XML 語法、更好的診斷、版本控制,以及多個檔案支援。

注意

Natvis 自訂會使用類別和結構,但不適用於 typedefs。

Natvis 視覺效果

您會使用 Natvis 架構來為您建立的類型建立視覺效果規則,以讓開發人員在偵錯期間更輕鬆地查看。

例如,下圖顯示偵錯工具視窗中型別為 Windows::UI::XAML::Controls::TextBox 的變數,但未套用任何自訂視覺效果。

TextBox 預設視覺效果

反白顯示的資料列會顯示 Text 類別的 TextBox 屬性。 複雜類別階層使得很難找到這個屬性。 偵錯工具不知道如何解譯自訂字串類型,因此您無法在文字方塊中看到保留的字串。

套用 Natvis 自訂視覺化檢視規則時,變數視窗中的相同 TextBox 外觀會比較簡單。 類別的所有重要成員會一併顯示,且偵錯工具會顯示自訂字串類型的基礎字串值。

使用視覺特效播放器的 TextBox 資料

在 C++ 專案中使用 .natvis 檔案

Natvis 會使用 .natvis 檔案來指定視覺效果規則。 .natvis 檔案是副檔名為 .natvis 的 XML 檔案。 Natvis 架構定義於 <VS 安裝資料夾>\Xml\Schemas\1033\natvis.xsd 中。

.natvis 檔案的基本結構是代表視覺效果項目的一或多個 Type 元素。 每個 Type 元素的完整名稱都會在其 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 會在 <VS 安裝資料夾>\Common7\Packages\Debugger\Visualizers 資料夾中提供一些 .natvis 檔案。 這些檔案具有許多常見類型的視覺效果規則,可作為針對新類型撰寫視覺效果的範例。

將 .natvis 檔案新增至 C++ 專案

您可以將 .natvis 檔案新增至任何 C++ 專案。

若要新增 .natvis 檔案:

  1. 在 [方案總管] 中選取 C++ 專案節點,然後選取[專案]>[新增專案],或以滑鼠右鍵按一下專案,然後選取 [新增]>[專案]

    如果您沒有看到所有項目範本,請選擇 [顯示所有範本]

  2. 在 [新增項目] 對話方塊中,選取 [Visual C++]>[公用程式]>[偵錯工具視覺效果檔案 (.natvis)]

  3. 命名檔案,並選取 [新增]

    新的檔案會新增至 [方案總管],並在 Visual Studio 文件窗格中開啟。

Visual Studio 偵錯工具會自動載入 C++ 專案中的 .natvis 檔案,且預設也會在專案建置時將其包含在 .pdb 檔案中。 如果您偵錯建置的應用程式,偵錯工具會從 .pdb 檔案載入 .natvis 檔案,即使您沒有開啟專案也一樣。 如果您不希望 .natvis 檔案包含在 .pdb 中,您可以將其從建置 的 .pdb 檔案中排除。

若要從 .pdb 排除 .natvis 檔案:

  1. 在 [方案總管] 中選取 .natvis 檔案,然後選取 [屬性] 圖示,或者以滑鼠右鍵按一下專案並選取 [屬性]

  2. 按下 [從組建排除] 旁的箭號展開下拉式清單,接著選取 [是],然後選取 [確定]

注意

若要偵錯可執行檔專案,請使用方案項目來新增任何不在 .pdb 中的 .natvis 檔案,因為沒有可用的 C++ 專案。

注意

.pdb 載入的 Natvis 規則僅適用於 .pdb 所參考模組中的類型。 例如,如果 Module1.pdb 有名為 Test 之類型的 Natvis 項目,則其只會套用至 Module1.dll 中的 Test 類別。 如果另一個模組也定義了名為 Test 的類別,則 Module1.pdb Natvis 項目就不會加以套用。

若要透過 VSIX 套件安裝和註冊 .natvis 檔案:

VSIX 套件可以安裝並註冊 .natvis 檔案。 無論安裝的位置為何,所有已註冊的 .natvis 檔案都會在偵錯期間自動挑選。

  1. 在 VSIX 套件中包含 .natvis 檔案。 例如,針對下列專案檔:

    <?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. source.extension.vsixmanifest 檔案中 註冊 .natvis 檔案:

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

Natvis 檔案位置

如果您要讓 .natvis 檔案套用至多個專案,可以將其加入使用者目錄或系統目錄。

.natvis 檔案會依下列順序進行評估:

  1. 您正在偵錯 .pdb 中內嵌的任何 .natvis 檔案,除非載入的專案中有相同名稱的檔案。

  2. 已載入 C++ 專案或最上層方案中的任何 .natvis 檔案。 此群組包含所有已載入的 C++ 專案,包括類別庫,但不包含其他語言的專案。

  3. 透過 VSIX 套件安裝並註冊的任何 .natvis 檔案。

  1. 使用者專屬的 Natvis 目錄 (例如,%USERPROFILE%\Documents\Visual Studio 2022\Visualizers)。
  1. 使用者專屬的 Natvis 目錄 (例如,%USERPROFILE%\Documents\Visual Studio 2019\Visualizers)。
  1. 系統範圍的 Natvis 目錄 (<Microsoft Visual Studio Installation 資料夾>\Common7\Packages\Debugger\Visualizers)。 此目錄具有隨 Visual Studio 一起安裝的 .natvis 檔案。 如果您有系統管理員權限,則可以將檔案新增至此目錄。

在偵錯時修改 .natvis 檔案

您可以在對 IDE 中的 .natvis 檔案進行修改時偵錯其專案。 在您要偵錯的 Visual Studio 執行個體中開啟檔案、加以修改,然後儲存。 只要已儲存檔案,[監看式] 和 [區域變數] 視窗會更新以反映此項變更。

您也可以在偵錯的方案中新增或刪除 .natvis 檔案,而 Visual Studio 會新增或移除相關的視覺效果。

偵錯時,您無法更新內嵌在 .pdb 檔案中的 .natvis 檔案。

如果您在 Visual Studio 外部修改 .natvis 檔案,則變更不會自動生效。 若要更新偵錯工具視窗,您可以在 [即時運算] 視窗中重新評估 .natvisreload 命令。 然後變更會生效,而不會重新開始偵錯工作階段。

此外也可以使用 .natvisreload 命令,將 .natvis 檔案升級為較新版本。 例如,.natvis 檔案可能會簽入原始檔控制,而您想要選取其他人最近所做的變更。

運算式和格式化

Natvis 視覺化使用 C++ 運算式來指定要顯示的資料項目。 除了偵錯工具中 C++ 運算式的增強功能和限制,如 CoNtext 運算子 (C++) 中所述,請注意下列事項:

  • Natvis 運算式是在正在視覺化之物件的內容 (而非目前的堆疊框架) 中進行評估。 例如,在 Natvis 運算式中,x 是指視覺化物件中名為 x 的欄位,而不是目前函式中名為 x 的區域變數。 您無法存取 Natvis 運算式中的區域變數,但可存取全域變數。

  • Natvis 運算式不允許函式評估或副作用。 系統會忽略函式呼叫和指派運算子。 由於偵錯工具內建函式沒有副作用,因此可自由地從任何 Natvis 運算式加以呼叫,即使不允許其他函式呼叫亦然。

  • 若要控制運算式的顯示方式,您可以使用 C++ 中的格式規範中所述的任何格式規範。 Natvis 在內部使用項目時會忽略格式規範,例如 ArrayItems 展開 (Expansion) 中的 Size 運算式。

注意

因為 Natvis 文件是 XML,所以您的運算式不能直接使用 ampersand、大於、小於或移位運算子。 您必須在項目本文和條件陳述式中逸出這些字元。 例如:
\<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\>

Natvis 檢視

您可以定義不同的 Natvis 檢視,以不同方式顯示類型。 例如,以下是 std::vector 的視覺效果,其定義名為 simple 的簡化檢視。 DisplayStringArrayItems 元素會顯示在預設檢視和 simple 檢視中,而 [size][capacity] 項目不會顯示在 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>

在 [監看式] 視窗中,使用 ,view 格式規範來指定替代檢視。 簡單檢視會顯示為 vec,view(simple)

包含簡單檢視的監看式視窗

Natvis 錯誤

當偵錯工具在視覺效果項目中遇到錯誤時,其會忽略錯誤。 它會以原始格式顯示類型,或挑選另一個合適的視覺效果。 您可以使用 Natvis 診斷來了解偵錯工具為何忽略視覺效果項目,以及查看基礎語法和剖析錯誤的原因。

若要開啟 Natvis 診斷:

  • 在 [工具]>[選項] (或 [偵錯]>[選項]) >[偵錯]>[輸出視窗] 中,將 [Natvis 診斷訊息 (僅限 C++)] 設定為 [錯誤]、[警告] 或 [詳細資訊],然後選取 [確定]

錯誤會出現在 [輸出] 視窗中。

Natvis 語法參考

下列元素和屬性可用於 Natvis 檔案中。

AutoVisualizer 項目

AutoVisualizer 元素是 .natvis 檔案的根節點,並且包含命名空間 xmlns: 屬性。

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

AutoVisualizer 元素可以有 TypeHResultUIVisualizerCustomVisualizer 子系。

Type 項目

基本 Type 看起來如下範例:

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

Type 元素會指定:

  1. 這個視覺效果應該用於哪種類型 ( Name 屬性)。

  2. 該類型物件的值外觀應該為何 ( DisplayString 項目)。

  3. 當使用者在變數視窗中展開類型時,類型成員的外觀應該為何 (Expand 節點)。

樣板化類別

Type 元素的 Name 屬性接受星號 * 作為可供樣板化類別名稱使用的萬用字元。

在下列範例中,不論物件是 CAtlArray<int>CAtlArray<float>,都使用相同的視覺效果。 如果針對 CAtlArray<float> 有特定視覺效果項目,則其優先順序高於一般項目。

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

您可以透過使用 $T1、$T2 等巨集,在視覺效果項目中參考範本參數。 若要尋找這些巨集的範例,請參閱 Visual Studio 隨附的 .natvis 檔案。

視覺化檢視類型比對

如果視覺效果項目無法驗證,系統會使用下一個可用的視覺效果。

Inheritable 屬性

選擇性 Inheritable 屬性會指定視覺效果僅套用至基底類型,還是套用至基底類型和所有衍生類型。 Inheritable 的預設值為 true

在下列範例中,此視覺效果只會套用至 BaseClass 類型:

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

Priority 屬性

如果定義無法剖析,則選擇性的 Priority 屬性會指定使用替代定義的順序。 Priority 的可能值為:LowMediumLowMediumMediumHighHigh。 預設值是 MediumPriority 屬性只會區分相同 .natvis 檔案內的優先順序。

下列範例會先剖析符合 2015 STL 的項目。 如果無法剖析,其會使用 2013 版 STL 的替代項目:

<!-- VC 2013 -->
<Type Name="std::reference_wrapper&lt;*&gt;" Priority="MediumLow">
     <DisplayString>{_Callee}</DisplayString>
    <Expand>
        <ExpandedItem>_Callee</ExpandedItem>
    </Expand>
</Type>

<!-- VC 2015 -->
<Type Name="std::reference_wrapper&lt;*&gt;">
    <DisplayString>{*_Ptr}</DisplayString>
    <Expand>
        <Item Name="[ptr]">_Ptr</Item>
    </Expand>
</Type>

Optional 屬性

您可以將 Optional 屬性放在任何節點上。 如果選擇性節點內的子運算式無法剖析,偵錯工具會忽略該節點,但會套用其餘的 Type 規則。 在下列類型中, [State] 是非選擇性的,但 [Exception] 是選擇性的。 如果 MyNamespace::MyClass 有名為 _M_exceptionHolder 的欄位,則 [State] 節點和 [Exception] 節點都會出現,但如果沒有 _M_exceptionHolder 欄位,則只會顯示 [State] 節點。

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

Condition 屬性

選擇性 Condition 屬性可供許多視覺效果項目使用,並指定何時要使用視覺效果規則。 如果條件屬性內的運算式解析為 false,則不會套用視覺效果規則。 如果評估為 true,或沒有任何 Condition 屬性,則會套用視覺效果。 您可以在視覺效果項目中針對 if-else 邏輯使用這個屬性。

例如,下列視覺效果為具有兩個智慧型指標類型的 DisplayString 元素。 當 _Myptr 成員為空時,會將第一個 DisplayString 元素的條件解析成 true,如此就可顯示表單。 當 _Myptr 成員不為空時,條件會評估為 false,且會顯示第二個 DisplayString 元素。

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

IncludeView 和 ExcludeView 屬性

IncludeViewExcludeView 屬性會指定在特定檢視中要顯示或不顯示的元素。 例如,在下列 std::vector 的 Natvis 規格中,simple 檢視不會顯示 [size][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>

您可以在類型和個別成員上使用 IncludeViewExcludeView 屬性。

Version 項目

Version 元素會將視覺效果項目的範圍設定為特定模組和版本。 Version 元素有助於避免名稱衝突、減少意外的不符合項目,並允許不同類型版本的不同視覺效果。

如果不同模組所使用的通用標頭檔定義了類型,則只有在類型位於指定的模組版本時,才會顯示已設定版本的視覺效果。

在下列範例中,視覺效果只適用於 Windows.UI.Xaml.dll 1.0 至 1.5 版中存在的 DirectUI::Border 類型。

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

您不需要 MinMax。 它們是選擇性屬性。 支援萬用字元。

Name 屬性的格式為 filename.ext,例如 hello.exesome.dll。 不允許路徑名稱。

DisplayString 元素

DisplayString 元素會指定要顯示為變數值的字串。 它接受與運算式混合的任意字串。 大括號內的所有項目都會解譯為運算式。 例如,下列 DisplayString 項目:

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

表示類型 CPoint 的變數會顯示為如下圖所示:

使用 DisplayString 元素

DisplayString 運算式中,xy (CPoint 的成員) 位於大括號中,因此會評估它們的值。 這個範例也會示範如何使用雙重大括號 ({{}}) 來逸出大括號。

注意

DisplayString 項目是接受任意字串和大括號語法的唯一項目。 所有其他視覺效果元素只接受偵錯工具可以評估的運算式。

StringView 元素

StringView 元素會定義偵錯工具可以傳送至內建文字視覺化檢視的值。 例如,假設有下列 ATL::CStringT 類型的視覺效果:

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

CStringT 物件會顯示在變數視窗中,如下列範例所示:

CStringT DisplayString 元素

新增 StringView 元素會告訴偵錯工具,它可以將值顯示為文字視覺效果。

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

在偵錯期間,您可以選取變數旁邊的放大鏡圖示,然後選取 [文字視覺化檢視] 以顯示 m_pszData 指向的字串。

使用 StringView 視覺特效播放器的 CStringT 資料

運算式 {m_pszData,su} 包含 C++ 格式規範 su 來以 Unicode 字串顯示值。 如需詳細資訊,請參閱 C++ 中的格式規範

Expand 元素

當您在變數視窗中展開類型時,選擇性 Expand 節點會自訂視覺化類型的子系。 Expand 節點接受定義子項目的子節點清單。

  • 如果未在視覺效果項目中指定 Expand 節點,子系會使用預設展開規則。

  • 如果指定下方沒有子節點的 Expand 節點,則類型在偵錯工具視窗中不是可展開的。

Item 展開

Item 元素是 Expand 節點中最基本和通用的元素。 Item 定義單一子項目。 例如,具有欄位 topleftrightbottomCRect 類別具有下列視覺效果項目:

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

在偵錯工具視窗中,CRect 類型看起來如同下列範例:

包含 Item 元素延伸模組的 CRect

偵錯工具會評估 WidthHeight 元素中指定的運算式,並在變數視窗的 [值] 資料行中顯示 值。

偵錯工具會自動為每個自訂展開建立 [未經處理的檢視] 節點。 上述螢幕擷取畫面顯示展開的 [未經處理的檢視] 節點,以顯示物件的預設未經處理的檢視與其 Natvis 視覺效果有何不同。 預設展開會建立基底類別的子樹狀結構,並列出基底類別的所有資料成員作為子系。

注意

如果 Item 項目的運算式指向複雜類型,則 [項目] 節點本身是可展開的。

Size

使用 ArrayItems 節點,讓 Visual Studio 偵錯工具將類型解譯為陣列並顯示其個別項目。 std::vector 的視覺化即為一個好例子:

<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 會顯示其個別項目:

std::使用 ArrayItems 擴充的向量

ArrayItems 節點必須具有:

  • 可讓偵錯工具了解陣列長度的 Size 運算式 (必須評估為整數)。
  • 指向第一個項目 (必須是不屬於 void* 之項目類型的指標) 的 ValuePointer 運算式。

該陣列的下限預設值為 0。 若要覆寫值,請使用 LowerBound 元素。 隨附於 Visual Studio 的 .natvis 檔案具有範例。

注意

例如,您可以使用如 vector[i][] 運算子搭配任何使用 ArrayItems 的單維度陣列視覺效果,即使類型本身 (例如 CATLArray) 不允許此運算子也是一樣。

您也可以指定多維度陣列。 在此情況下,偵錯工具需要多一點資訊才能正確顯示子項目:

<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 會指定陣列是以資料列還是以資料行作為主要次序。
  • Rank 指定陣列的陣序。
  • Size 元素接受隱含的 $i 參數,並將參數取代為維度索引,以便在該維度中尋找陣列的長度。
    • 在上一個範例中,運算式 _M_extent.M_base[0] 應提供第 0 維度的長度,_M_extent._M_base[1] 應提供第 1 維度的長度,依此類推。
  • LowerBound 會指定陣列中每個維度的下限。 對於多維度陣列,您可以指定使用隱含 $i 參數的運算式。 $i 參數將會取代為維度索引,以在該維度中尋找陣列的下限。
    • 在上一個範例中,所有維度都會從 0 開始。 不過,如果您有 ($i == 1) ? 1000 : 100 作為下限,第 0 維度會從 100 開始,第 1 維度會從 1000 開始。
      • ,例如 [100, 1000], [100, 1001], [100, 1002], ... [101, 1000], [101, 1001],...

以下是二維 Concurrency::array 物件在偵錯工具中的外觀:

包含 ArrayItems 擴充的二維陣列

IndexListItems 展開

您僅能在陣列元素於記憶體中連續配置時,才能使用 ArrayItems 展開。 偵錯工具只是遞增其指標,即可到達下一個項目。 如果您需要操作值節點的索引,請使用 IndexListItems 節點。 以下是具有 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>

ArrayItemsIndexListItems 之間的唯一差異在於 ValueNode,其預期第 i 個元素的完整運算式具有隱含 $i 參數。

注意

例如,您可以使用如 vector[i][] 運算子搭配任何使用 IndexListItems 的單維度陣列視覺效果,即使類型本身 (例如 CATLArray) 不允許此運算子也是一樣。

LinkedListItems 展開

如果視覺化類型代表連結清單,則偵錯工具可以使用 LinkedListItems 節點顯示其子系。 CAtlList 類型的下列視覺效果會使用 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>

Size 項目參考清單的長度。 HeadPointer 指向第一個項目, NextPointer 參考下一個項目,而 ValueNode 參考項目的值。

偵錯工具會評估 LinkedListItems 節點元素內容中的 NextPointerValueNode 運算式,而不是父清單類型。 在上述範例中,CAtlList 具有一個 CNode 類別 (可在 atlcoll.h 中找到),該類別是連結清單的節點。 m_pNextm_element 是該 CNode 類別的欄位,而不是 CAtlList 類別的欄位。

ValueNode 可以保留為空白,或使用 this 來參考 LinkedListItems 節點本身。

CustomListItems 展開

CustomListItems 展開可讓您撰寫周遊資料結構 (例如雜湊表) 的自訂邏輯。 使用 CustomListItems 來視覺化資料結構,以針對您需要評估的所有內容使用 C++ 運算式,但不適合 ArrayItemsIndexListItemsLinkedListItems 的模式。

您可以使用 ExecCustomListItems 展開內執行程式碼,並使用展開中定義的變數和物件。 您可以搭配 Exec 使用邏輯運算子、算術運算子和指派運算子。 除了 C++ 運算式評估工具所支援的偵錯工具內建函式之外,您無法使用 Exec 來評估函式。

下列 CustomListItems 的視覺化檢視為 CAtlMap 適用時的絕佳範例。

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

TreeItems 展開

如果視覺化類型代表樹狀結構,則偵錯工具可以使用 TreeItems 節點查核樹狀結構並顯示其子系。 以下是使用 TreeItems 節點之 std::map 類型的視覺效果:

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

其語法與 LinkedListItems 節點的語法非常類似。 LeftPointerRightPointerValueNode 會在樹狀節點類別的內容下進行評估。 ValueNode 可以保留為空白,或使用 this 來參考 TreeItems 節點本身。

ExpandedItem 展開

ExpandedItem 元素會產生彙總子檢視,方法是將基底類別或資料成員的屬性當做視覺化類型的子系來顯示。 偵錯工具會評估指定的運算式,並將結果的子節點附加至視覺化類型的子清單。

例如,智慧型指標類型 auto_ptr<vector<int>> 通常會顯示為:

auto_ptr<vector<int>> 預設擴充

若要查看向量的值,您必須在變數視窗中通過 _Myptr 成員,向下切入兩個層級。 藉由新增 ExpandedItem 元素,您可以從階層中排除 _Myptr 變數,並直接檢視向量元素:

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

auto_ptr<vector<int>> ExpandedItem 擴充

下列範例示範如何從衍生類別中的基底類別彙總屬性。 假設 CPanel 類別衍生自 CFrameworkElementExpandedItem 節點不會重複來自基底 CFrameworkElement 類別的屬性,而是將這些屬性附加至 CPanel 類別的子清單。

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

此處需要 nd 格式規範,以關閉衍生類別的視覺化比對。 否則,運算式 *(CFrameworkElement*)this 會導致重新套用 CPanel 視覺效果,因為預設視覺效果類型比對規則會將其視為最適當的運算式。 使用 nd 格式規範來指示偵錯工具使用基類視覺效果,如果基類沒有視覺效果,則為預設展開。

Synthetic 項目展開

其中 ExpandedItem 元素透過排除階層來提供更平面的資料檢視, Synthetic 節點則相反。 它可讓您建立不是運算式結果的人工子項目。 人工元素可以有自己的子項目。 在下列範例中, Concurrency::array 類型的視覺化使用 Synthetic 節點向使用者顯示診斷訊息:

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

Concurrency::包含 Synthetic 元素擴充的陣列

內建的擴充

可從運算式呼叫的自訂內建函式。 <Intrinsic> 元素必須隨附偵錯工具元件,以透過 IDkmIntrinsicFunctionEvaluator140 介面實作函式。

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

HResult 元素

HResult 元素可讓您自訂偵錯工具視窗中針對 HRESULT 所顯示的資訊。 HRValue 元素必須包含要自訂的 32 位元 HRESULT 值。 HRDescription 元素包含要在偵錯工具視窗中顯示的資訊。


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

UIVisualizer 元素

UIVisualizer 項目會向偵錯工具註冊圖形視覺化檢視外掛程式。 圖形化視覺化檢視會建立對話方塊或其他介面,以符合其資料類型的方式顯示變數或物件。 視覺化檢視外掛程式必須撰寫為 VSPackage,且必須公開偵錯工具可以使用的服務。 .natvis 檔案包含外掛模組的註冊資訊,例如其名稱、公開服務的全域唯一識別碼 (GUID),及其可視覺化的型別。

以下是 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>
  • ServiceId - Id 屬性組會識別 UIVisualizerServiceId 是視覺化檢視套件所公開服務的 GUID。 Id 是一個唯一識別碼,可區分服務可能提供的多個視覺化檢視。 在上述範例中,相同視覺化檢視服務提供兩個視覺化檢視。

  • MenuName 屬性會定義視覺特效播放器名稱,以顯示在偵錯工具放大鏡圖示旁邊的下拉式清單中。 例如:

    UIVisualizer 功能表快速鍵選單

.natvis 檔案中定義的每個類型都必須明確列出可以顯示它的任何 UI 視覺化檢視。 偵錯工具會使用已註冊視覺化檢視比對類型項目中的視覺化檢視參考。 例如,在上述範例中,下列 std::vector 的類型項目會參考 UIVisualizer

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

您可以在用來檢視記憶體內部點陣圖的影像監看延伸模組中看到的 UIVisualizer 範例。

CustomVisualizer 元素

CustomVisualizer 是一個擴充點,其會指定您在 Visual Studio Code 中撰寫以控制視覺效果的 VSIX 延伸項目。 如需撰寫 VSIX 延伸模組的詳細資訊,請參閱 Visual Studio SDK

撰寫自訂視覺化檢視比 XML Natvis 定義需要更多工作,但您可以不必考慮 Natvis 支援或不支援的限制。 自訂視覺化檢視可以存取完整的偵錯工具擴充性 API 集,這可用於查詢和修改偵錯項目處理序或與 Visual Studio 的其他組件通訊。

您可以在 CustomVisualizer 元素上使用 ConditionIncludeViewExcludeView 屬性。

限制

Natvis 自訂會使用類別和結構,但不適用於 typedefs。

Natvis 不支援基本類型的視覺化檢視 (例如 intbool) 或基本類型的指標。 在此案例中,有一個選項是使用適合您使用案例的格式規範。 例如,如果您在程式碼中使用 double* mydoublearray,則可以在偵錯工具的 [監看式] 視窗中使用陣列格式規範,例如運算式 mydoublearray, [100],其中會顯示前 100 個元素。