使用 Natvis 架構在調試程式中建立C++物件的自定義檢視
Visual Studio Natvis 架構會自定義原生類型出現在調試程式變數視窗中的方式,例如 局部變數 和 Watch 視窗,以及 DataTips。 Natvis 視覺化工具可協助您在偵錯期間令您建立的類型更為顯著。
Natvis 會將舊版 Visual Studio 中的 autoexp.dat 檔案取代為 XML 語法、更好的診斷、版本控制,以及多個檔案支援。
注意
Natvis 自定義會使用類別和結構,但不適用於 typedefs。
Natvis 視覺化
您可以使用 Natvis 架構來建立您所建立類型的視覺效果規則,讓開發人員在偵錯期間更容易看到這些規則。
例如,下圖顯示調試程式視窗中類型為 Windows::UI::XAML::Controls::TextBox 的變數,而不會套用任何自定義視覺效果。
醒目標示的列顯示 TextBox
類別的 Text
屬性。 複雜類別階層使得很難找到這個屬性。 調試程式不知道如何解譯自定義字串類型,因此您無法在文字框中看到保留的字串。
套用 Natvis 自定義可視化檢視規則時,相同的 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\Visualrs 資料夾中提供一些 .natvis 檔案。 這些檔案有許多常見類型的視覺效果規則,而且可作為撰寫新類型視覺效果的範例。
將 .natvis 檔案新增至C++專案
您可以將 .natvis 檔案新增至任何C++專案。
若要新增 .natvis 檔案:
選取 [方案總管]中的 [C++專案] 節點,然後選取 [專案]>[新增專案],或以滑鼠右鍵按兩下專案,然後選取 [[新增>新專案]。
如果您沒有看到所有項目樣本,請選擇 [顯示所有範本。
在 [[新增專案] 對話框中,選取 [Visual C++>公用程式>調試程序視覺效果檔案 [.natvis]。
為檔案命名,然後選取 [新增]。
新檔案會新增至 [方案總管],並在 Visual Studio 檔窗格中開啟。
Visual Studio 調試程式會自動載入C++專案中 .natvis 檔案,而且預設也會在專案建置時包含在 .pdb 檔案中。 如果您偵錯建置的應用程式,調試程式會從 .pdb 檔案載入 .natvis 檔案,即使您沒有開啟專案也一樣。 如果您不希望 .natvis 檔案包含在 .pdb中,您可以將它從建置 的 .pdb 檔案中排除。
若要從 .pdb排除 .natvis 檔案:
在 [方案總管] 中選取.natvis 檔案,然後選取 [屬性] 圖示,或以滑鼠右鍵按一下檔案,然後選取 [屬性]。
下拉 [從組建中排除] 旁的箭頭,然後選取 [是],然後選取 [確定]。
注意
若要偵錯可執行的專案,請使用方案專案來新增任何 .natvis 檔案,而檔案不在 .pdb中,因為沒有可用的專案C++。
注意
從 .pdb 載入的 Natvis 規則 僅適用於 .pdb 所參考之模組中的類型。 例如,如果 Module1.pdb 具有一個名為 Test
的類型的 Natvis 項目,它只適用於 Module1.dll中的 Test
類別。 如果另一個模組也定義名為 Test
的類別,則 Module1.pdb Natvis 專案不適用於它。
若要透過 VSIX 套件安裝及註冊 .natvis 檔案:
VSIX 套件可以安裝並註冊 .natvis 檔案。 不論安裝在哪裡,所有已註冊的 .natvis 檔案在偵錯期間都會被自動偵測到。
在 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>
在 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 檔案會依照下列順序進行評估:
任何 .natvis 檔案內嵌在您正在偵錯的 .pdb 中,除非在載入的專案中已存在相同名稱的檔案。
載入的 C++ 專案或最上層解決方案中的任何 .natvis 檔案。 此群組包含所有載入C++專案,包括類別庫,但不包含其他語言的專案。
任何透過 VSIX 套件安裝及註冊的 .natvis 檔案。
- 使用者特定的 Natvis 目錄(例如,%USERPROFILE%\Documents\Visual Studio 2022\Visualizers)。
- 使用者特定的 Natvis 目錄 (例如,%USERPROFILE%\Documents\Visual Studio 2019\Visualizers)。
- 全系統的 Natvis 目錄 (<Microsoft Visual Studio 安裝資料夾>\Common7\Packages\Debugger\Visualizers)。 此目錄具有與 Visual Studio 一起安裝的 .natvis 檔案。 如果您有系統管理員許可權,您可以將檔案新增至此目錄。
偵錯時修改 .natvis 檔案
您可以在 IDE 偵錯其專案時修改 .natvis 檔案。 在您正在偵錯的同一個 Visual Studio 實例中開啟檔案,進行修改,並儲存檔案。 儲存檔案之後,Watch 和 [局部變數] 視窗更新,以反映變更。
您也可以在偵錯的解決方案中新增或刪除 .natvis 檔案,而 Visual Studio 會新增或移除相關的視覺效果。
偵錯時,您無法更新內嵌在 .pdb .pdb 檔案中的 .natvis .natvis 檔案。
如果您在 Visual Studio 外部修改 .natvis 檔案,則變更不會自動生效。 若要更新偵錯視窗,您可以在 [即時] 視窗中重新評估 .natvisreload 命令。 然後變更會生效,而不會重新啟動偵錯會話。
也請使用 .natvisreload 命令,將 .natvis 檔案升級為較新版本。 例如,.natvis 檔案可能會提交到原始檔控制系統,而您想要取得他人最近所做的變更。
表達式和格式設定
Natvis 視覺效果會使用C++運算式來指定要顯示的數據項。 除了「Context 運算符(C++)」中所描述的調試程式中C++表達式的增強功能和限制之外,請注意以下事項:
Natvis 表達式會在要可視化的物件內容中進行評估,而不是目前的堆疊框架。 例如,Natvis 運算式中的
x
是指物件中名為 x 的字段,而不是目前函式中名為 x 的局部變數。 雖然您可以存取全域變數,但您無法存取 Natvis 運算式中的局部變數。Natvis 運算式不允許函數求值或引發副作用。 函式呼叫和指派運算符會被忽略。 因為 調試程式內部函式 沒有副作用,所以即使不允許其他函數調用,也可能從任何 Natvis 表達式自由呼叫它們。
若要控制表達式的顯示方式,您可以使用 C++ 中格式規範中所述的任何格式規範。 Natvis 在內部使用條目時,會忽略格式指定符,例如
Size
表達式在 ArrayItems 展開中被忽略。
注意
因為 Natvis 檔是 XML,所以您的表示式不能直接使用 ampersand、大於、小於或 shift 運算符。 您必須在專案本文和 condition 語句中逸出這些字元。 例如:
\<Item Name="HiByte"\>(byte)(_flags \>\> 24),x\</Item\>
\<Item Name="HiByteStatus" Condition="(_flags \& 0xFF000000) == 0"\>"None"\</Item\>
\<Item Name="HiByteStatus" Condition="(_flags \& 0xFF000000) != 0"\>"Some"\</Item\>
Natvis 視圖
您可以定義不同的 Natvis 檢視,以不同方式顯示類型。 例如,以下是 std::vector
的視覺效果,其定義了一個名為 simple
的簡化檢視。
DisplayString
和 ArrayItems
元素會顯示在默認檢視和 simple
檢視中,而 [size]
和 [capacity]
專案不會顯示在 simple
檢視中。
<Type Name="std::vector<*>">
<DisplayString>{{ size={_Mylast - _Myfirst} }}</DisplayString>
<Expand>
<Item Name="[size]" ExcludeView="simple">_Mylast - _Myfirst</Item>
<Item Name="[capacity]" ExcludeView="simple">_Myend - _Myfirst</Item>
<ArrayItems>
<Size>_Mylast - _Myfirst</Size>
<ValuePointer>_Myfirst</ValuePointer>
</ArrayItems>
</Expand>
</Type>
在 [監看式] 視窗中,使用 、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
元素可以有 Type、HResult、UIVisualizer和 CustomVisualizer 子系。
類型元素
基本 Type
看起來像下列範例:
<Type Name="[fully qualified type name]">
<DisplayString Condition="[Boolean expression]">[Display value]</DisplayString>
<Expand>
...
</Expand>
</Type>
Type
元素會指定:
應該使用哪種類型的視覺化(
Name
屬性)。該型別的物件值應該像什麼樣子(
DisplayString
元素)。當使用者在變數視窗中展開類型時,類型的成員看起來應該像這樣(
Expand
節點)。
樣板化類別
Type
項目的 Name
屬性會接受星號 *
做為通配符,可用於樣板化類別名稱。
在下列範例中,不論對像是 CAtlArray<int>
還是 CAtlArray<float>
,都使用相同的視覺效果。 如果 CAtlArray<float>
有特有的視覺化項目,會優先於一般項目。
<Type Name="ATL::CAtlArray<*>">
<DisplayString>{{Count = {m_nSize}}}</DisplayString>
</Type>
您可以使用巨集$T 1、$T 2 等等,在視覺效果項目中參考範本參數。 若要尋找這些巨集的範例,請參閱隨附於Visual Studio的 .natvis 檔案。
可視化檢視類型比對
如果視覺效果項目無法驗證,則會使用下一個可用的視覺效果。
可繼承的屬性
選擇性 Inheritable
屬性會指定視覺效果僅適用於基底類型,還是套用至基底型別和所有衍生型別。
Inheritable
預設值為 true
。
在下列範例中,視覺效果僅適用於 BaseClass
類型:
<Type Name="Namespace::BaseClass" Inheritable="false">
<DisplayString>{{Count = {m_nSize}}}</DisplayString>
</Type>
優先順序屬性
選擇性 Priority
屬性會指定當定義無法剖析時,使用替代定義的順序。
Priority
的可能值為:Low
、MediumLow
、Medium
、MediumHigh
和 High
。 預設值為 Medium
。
Priority
屬性只會區分相同 .natvis 檔案內的優先順序。
下列範例會先解析符合 2015 STL 的條目。 如果無法解析,它會使用 2013 版 STL 的替代項:
<!-- VC 2013 -->
<Type Name="std::reference_wrapper<*>" Priority="MediumLow">
<DisplayString>{_Callee}</DisplayString>
<Expand>
<ExpandedItem>_Callee</ExpandedItem>
</Expand>
</Type>
<!-- VC 2015 -->
<Type Name="std::reference_wrapper<*>">
<DisplayString>{*_Ptr}</DisplayString>
<Expand>
<Item Name="[ptr]">_Ptr</Item>
</Expand>
</Type>
選擇性屬性
您可以將 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
屬性可用於許多視覺效果元素,並指定何時使用視覺效果規則。 如果條件屬性內的表達式解析為 false
,則不適用視覺效果規則。 如果評估結果為 true
,或不存在 Condition
屬性,則將套用視覺化。 您可以在視覺效果專案中將此屬性用於 if-else 邏輯。
例如,下列視覺化包含兩個智慧指標類型的 DisplayString
元素。 當 _Myptr
成員是空的時,第一個 DisplayString
元素的條件會解析為 true
,讓窗體顯示。 當 _Myptr
成員不是空的時,條件會評估為 false
,而第二個 DisplayString
項目會顯示。
<Type Name="std::auto_ptr<*>">
<DisplayString Condition="_Myptr == 0">empty</DisplayString>
<DisplayString>auto_ptr {*_Myptr}</DisplayString>
<Expand>
<ExpandedItem>_Myptr</ExpandedItem>
</Expand>
</Type>
IncludeView 和 ExcludeView 屬性
IncludeView
和 ExcludeView
屬性會指定要在特定檢視中顯示或不顯示的元素。 例如,在下列 std::vector
Natvis 描述中,simple
檢視不會顯示 [size]
和 [capacity]
項目。
<Type Name="std::vector<*>">
<DisplayString>{{ size={_Mylast - _Myfirst} }}</DisplayString>
<Expand>
<Item Name="[size]" ExcludeView="simple">_Mylast - _Myfirst</Item>
<Item Name="[capacity]" ExcludeView="simple">_Myend - _Myfirst</Item>
<ArrayItems>
<Size>_Mylast - _Myfirst</Size>
<ValuePointer>_Myfirst</ValuePointer>
</ArrayItems>
</Expand>
</Type>
您可以在類型和個別成員上使用 IncludeView
和 ExcludeView
屬性。
Version 元素
Version
元素會將視覺化條目的範圍限制於特定模組和版本。
Version
元素有助於避免名稱衝突、減少意外匹配錯誤,並允許不同類型版本擁有不同的可視化。
如果不同模組所使用的通用頭檔定義類型,則只有在類型位於指定的模組版本時,才會顯示版本設定的視覺效果。
在下列範例中,視覺效果僅適用於從1.0版到1.5版 Windows.UI.Xaml.dll
中找到的 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>
您不需要 Min
與 Max
。 它們是選擇性屬性。 無法支援通配符。
Name
屬性是 的格式,為 filename.ext,例如 hello.exe 或 some.dll。 不允許路徑名稱。
DisplayString 元素
DisplayString
元素指定顯示為變數值的字串。 它會接受與表達式混合的任意字串。 大括弧內的一切會解譯為表達式。 例如,下列 DisplayString
項目:
<Type Name="CPoint">
<DisplayString>{{x={x} y={y}}}</DisplayString>
</Type>
表示類型為 CPoint
的變數會顯示如下圖所示:
在 DisplayString
表示式中,x
和 y
,其為 CPoint
的成員,位於大括弧內,因此會評估其值。 此範例也會示範如何使用雙大括弧來逸出大括弧({{
或 }}
)。
注意
DisplayString
元素是唯一接受任意字串和大括號語法的元素。 所有其他視覺效果元素只接受調試程式可以評估的表達式。
StringView 元素
StringView
元素會定義調試程式可以傳送至內建文字可視化檢視的值。 例如,假設 ATL::CStringT
類型的下列視覺效果:
<Type Name="ATL::CStringT<wchar_t,*>">
<DisplayString>{m_pszData,su}</DisplayString>
</Type>
CStringT
物件會顯示在變數視窗中,例如下列範例:
新增 StringView
元素會告訴調試程式,它可以將值顯示為文字視覺效果。
<Type Name="ATL::CStringT<wchar_t,*>">
<DisplayString>{m_pszData,su}</DisplayString>
<StringView>m_pszData,su</StringView>
</Type>
在偵錯期間,您可以選取變數旁的放大鏡圖示,然後選取 文字可視化檢視 以顯示 m_pszData 指向的字串。
表達式 {m_pszData,su}
包含 C++ 格式規範 su,以將值顯示為 Unicode 字串。 如需詳細資訊,請參閱 C++ 中的格式規範。
展開元素
當您在變數視窗中展開類型時,選擇性 Expand
節點會自定義可視化類型的子系。
Expand
節點接受定義子元素的子節點清單。
如果未在視覺效果專案中指定
Expand
節點,子系就會使用預設的展開規則。如果指定了
Expand
節點,且其下沒有子節點,則類型無法在調試程式視窗中展開。
項目展開
Item
元素是 Expand
節點中最基本的和通用元素。
Item
定義單一子元素。 例如,具有欄位 top
、left
、right
和 bottom
的 CRect
類別具有以下視覺化條目:
<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
類型看起來像下列範例:
調試程式會評估 Width
和 Height
專案中指定的表示式,並在變數視窗的 Value 資料行中顯示值。
調試程式會自動為每個自定義擴充建立 [原始檢視] 節點。 上述螢幕快照顯示展開 [原始檢視] 節點,以顯示對象的預設原始檢視與其 Natvis 視覺效果有何不同。 默認展開會建立基類的子樹,並將基類的所有數據成員列為子類別。
注意
如果專案項目的表達式指向複雜類型,則 Item 節點本身是可展開的。
ArrayItems 擴充
使用 ArrayItems
節點讓 Visual Studio 調試器將類型解釋為陣列,並顯示其個別元素。
std::vector
的視覺效果是一個很好的範例:
<Type Name="std::vector<*>">
<DisplayString>{{size = {_Mylast - _Myfirst}}}</DisplayString>
<Expand>
<Item Name="[size]">_Mylast - _Myfirst</Item>
<Item Name="[capacity]">(_Myend - _Myfirst)</Item>
<ArrayItems>
<Size>_Mylast - _Myfirst</Size>
<ValuePointer>_Myfirst</ValuePointer>
</ArrayItems>
</Expand>
</Type>
在變數視窗中展開時,std::vector
會顯示其個別元素:
ArrayItems
節點必須具有:
- 調試器需要一個
Size
表達式(必須評估為整數),用來理解陣列的長度。 - 指向第一個元素的
ValuePointer
表達式(其元素類型的指標不得是void*
)。
陣列下限的預設值為 0。 若要覆寫值,請使用 LowerBound
元素。 隨附於Visual Studio的 .natvis 檔案有範例。
注意
您可以使用 []
運算符,例如 vector[i]
,搭配任何使用 ArrayItems
的單維數位視覺效果,即使類型本身 (例如 CATLArray
) 不允許此運算符。
您也可以指定多維度陣列。 在此情況下,調試程式需要稍微更多資訊才能正確顯示子元素:
<Type Name="Concurrency::array<*,*>">
<DisplayString>extent = {_M_extent}</DisplayString>
<Expand>
<Item Name="extent">_M_extent</Item>
<ArrayItems Condition="_M_buffer_descriptor._M_data_ptr != 0">
<Direction>Forward</Direction>
<Rank>$T2</Rank>
<Size>_M_extent._M_base[$i]</Size>
<ValuePointer>($T1*) _M_buffer_descriptor._M_data_ptr</ValuePointer>
<LowerBound>0</LowerBound>
</ArrayItems>
</Expand>
</Type>
-
Direction
指定陣列是以行優先或列優先的順序。 -
Rank
指定陣列的排名。 -
Size
元素接受隱含的$i
參數,它以維度索引替換,從而在該維度中找到陣列的長度。- 在上一個範例中,表達式
_M_extent.M_base[0]
應該提供第0個維度的長度,_M_extent._M_base[1]
第一個維度等等。
- 在上一個範例中,表達式
-
LowerBound
會指定陣列每個維度的下限。 對於多維度陣列,您可以指定使用隱含$i
參數的運算式。$i
參數會取代維度索引,以在該維度中尋找陣列的下限。- 在上一個範例中,所有維度都會從 0 開始。 不過,如果您
($i == 1) ? 1000 : 100
為下限,第0個維度會從100開始,第一個維度會從1000開始。- ,例如
[100, 1000], [100, 1001], [100, 1002], ... [101, 1000], [101, 1001],...
- ,例如
- 在上一個範例中,所有維度都會從 0 開始。 不過,如果您
以下是二維 Concurrency::array
物件在調試程式視窗中的外觀:
IndexListItems 擴充
只有當陣列元素在記憶體中連續配置時,才可以使用 ArrayItems
擴充。 偵錯器只需遞增其指標,即可到達下一個元素。 如果您需要操作索引以到達值節點,請使用 IndexListItems
節點。 以下是展示 IndexListItems
節點的視覺化:
<Type Name="Concurrency::multi_link_registry<*>">
<DisplayString>{{size = {_M_vector._M_index}}}</DisplayString>
<Expand>
<Item Name="[size]">_M_vector._M_index</Item>
<IndexListItems>
<Size>_M_vector._M_index</Size>
<ValueNode>*(_M_vector._M_array[$i])</ValueNode>
</IndexListItems>
</Expand>
</Type>
ArrayItems
與 IndexListItems
之間的唯一差異是 ValueNode
,其預期完整表達式涉及到第個 i 元素,並包含隱含的 $i
參數。
注意
您可以使用 []
運算符,例如 vector[i]
,搭配任何使用 IndexListItems
的單維數位視覺效果,即使類型本身 (例如 CATLArray
) 不允許此運算符。
LinkedListItems 擴充
如果可視化類型代表連結清單,調試程式可以使用 LinkedListItems
節點來顯示其子系。
CAtlList
類型的下列視覺效果使用 LinkedListItems
:
<Type Name="ATL::CAtlList<*,*>">
<DisplayString>{{Count = {m_nElements}}}</DisplayString>
<Expand>
<Item Name="Count">m_nElements</Item>
<LinkedListItems>
<Size>m_nElements</Size>
<HeadPointer>m_pHead</HeadPointer>
<NextPointer>m_pNext</NextPointer>
<ValueNode>m_element</ValueNode>
</LinkedListItems>
</Expand>
</Type>
Size
元素是指清單的長度。
HeadPointer
指向第一個專案,NextPointer
參考下一個專案,ValueNode
參考專案的值。
偵錯器會在節點元素 LinkedListItems
的上下文中評估 NextPointer
和 ValueNode
表達式,而不是父清單類型。 在上述範例中,CAtlList
具有作為連結清單節點的 CNode
類別(位於 atlcoll.h
中)。
m_pNext
和 m_element
是該 CNode
類別的欄位,而不是 CAtlList
類別的欄位。
ValueNode
可以保留空白,或使用 this
來參考 LinkedListItems
節點本身。
CustomListItems 擴充
CustomListItems
擴充可讓您撰寫自定義邏輯來遍歷資料結構,例如散列表。 使用 CustomListItems
來可視化數據結構,這些結構可以使用 C++ 運算式來進行您所需的一切評估功能,但不太適合套入 ArrayItems
、IndexListItems
或 LinkedListItems
的模式。
您可以使用 Exec
,使用擴充中定義的變數和物件,在 CustomListItems
展開內執行程序代碼。 您可以使用邏輯運算子、算術運算子和指派運算子搭配 Exec
。 您無法使用 Exec
來評估函式,C++ 表達式評估工具支援的 偵錯器內建函式 除外。
以下 CAtlMap
的可視化是 CustomListItems
適用的絕佳範例。
<Type Name="ATL::CAtlMap<*,*,*,*>">
<AlternativeType Name="ATL::CMapToInterface<*,*,*>"/>
<AlternativeType Name="ATL::CMapToAutoPtr<*,*,*>"/>
<DisplayString>{{Count = {m_nElements}}}</DisplayString>
<Expand>
<CustomListItems MaxItemsPerView="5000" ExcludeView="Test">
<Variable Name="iBucket" InitialValue="-1" />
<Variable Name="pBucket" InitialValue="m_ppBins == nullptr ? nullptr : *m_ppBins" />
<Variable Name="iBucketIncrement" InitialValue="-1" />
<Size>m_nElements</Size>
<Exec>pBucket = nullptr</Exec>
<Loop>
<If Condition="pBucket == nullptr">
<Exec>iBucket++</Exec>
<Exec>iBucketIncrement = __findnonnull(m_ppBins + iBucket, m_nBins - iBucket)</Exec>
<Break Condition="iBucketIncrement == -1" />
<Exec>iBucket += iBucketIncrement</Exec>
<Exec>pBucket = m_ppBins[iBucket]</Exec>
</If>
<Item>pBucket,na</Item>
<Exec>pBucket = pBucket->m_pNext</Exec>
</Loop>
</CustomListItems>
</Expand>
</Type>
TreeItems 擴充
如果可視化類型代表樹狀結構,調試程式可以使用 TreeItems
節點來遍歷樹狀結構並顯示其子節點。 以下是 std::map
類型透過 TreeItems
節點的視覺化:
<Type Name="std::map<*>">
<DisplayString>{{size = {_Mysize}}}</DisplayString>
<Expand>
<Item Name="[size]">_Mysize</Item>
<Item Name="[comp]">comp</Item>
<TreeItems>
<Size>_Mysize</Size>
<HeadPointer>_Myhead->_Parent</HeadPointer>
<LeftPointer>_Left</LeftPointer>
<RightPointer>_Right</RightPointer>
<ValueNode Condition="!((bool)_Isnil)">_Myval</ValueNode>
</TreeItems>
</Expand>
</Type>
語法類似於 LinkedListItems
節點。
LeftPointer
、RightPointer
和 ValueNode
會在樹狀節點類別的內容下進行評估。
ValueNode
可以保留空白,或使用 this
來參考 TreeItems
節點本身。
展開項目擴充
ExpandedItem
元素會藉由顯示基類或資料成員的屬性,將其視為可視化類型的子層級,來產生匯總的子檢視。 調試程式會評估指定的表達式,並將結果的子節點附加至可視化類型的子清單。
例如,智慧型指標類型 auto_ptr<vector<int>>
通常會顯示為:
若要查看向量的值,您必須在變數視窗中向下探索兩個層級,經過 _Myptr
成員。 藉由新增 ExpandedItem
元素,您可以從階層中排除 _Myptr
變數,並直接檢視向量元素:
<Type Name="std::auto_ptr<*>">
<DisplayString>auto_ptr {*_Myptr}</DisplayString>
<Expand>
<ExpandedItem>_Myptr</ExpandedItem>
</Expand>
</Type>
下列範例示範如何匯總衍生類別中基類的屬性。 假設 CPanel
類別衍生自 CFrameworkElement
。
ExpandedItem
節點視覺效果會將這些屬性附加至 CPanel
類別的子清單,而不是重複來自基底 CFrameworkElement
類別的屬性。
<Type Name="CPanel">
<DisplayString>{{Name = {*(m_pstrName)}}}</DisplayString>
<Expand>
<Item Name="IsItemsHost">(bool)m_bItemsHost</Item>
<ExpandedItem>*(CFrameworkElement*)this,nd</ExpandedItem>
</Expand>
</Type>
在此需要 和 格式規範,它會關閉衍生類別的視覺化匹配。 否則,表達式 *(CFrameworkElement*)this
會導致再次套用 CPanel
視覺效果,因為預設視覺效果類型比對規則會將其視為最適當的視覺效果。 使用 nd 格式規範指示調試程式使用基類視覺效果,或基類沒有視覺效果時的預設展開。
綜合項目展開
雖然 ExpandedItem
元素藉由排除階層來提供較平淡的數據檢視,但 Synthetic
節點會相反。 它可讓您建立不是由表達式產生的人工子元素。 人工元素可以有自己的子元素。 在下列範例中,Concurrency::array
類型的視覺效果會使用 Synthetic
節點向使用者顯示診斷訊息:
<Type Name="Concurrency::array<*,*>">
<DisplayString>extent = {_M_extent}</DisplayString>
<Expand>
<Item Name="extent" Condition="_M_buffer_descriptor._M_data_ptr == 0">_M_extent</Item>
<ArrayItems Condition="_M_buffer_descriptor._M_data_ptr != 0">
<Rank>$T2</Rank>
<Size>_M_extent._M_base[$i]</Size>
<ValuePointer>($T1*) _M_buffer_descriptor._M_data_ptr</ValuePointer>
</ArrayItems>
<Synthetic Name="Array" Condition="_M_buffer_descriptor._M_data_ptr == 0">
<DisplayString>Array members can be viewed only under the GPU debugger</DisplayString>
</Synthetic>
</Expand>
</Type>
內在擴展
可從表達式呼叫的自定義內部函數。
<Intrinsic>
元素必須隨附一個除錯器元件,透過 IDkmIntrinsicFunctionEvaluator140 介面實作該函式。 如需實作自訂內部函數的詳細資訊,請參閱 實作 NatVis 自定義內部函數。
<Type Name="std::vector<*>">
<Intrinsic Name="size" Expression="(size_t)(_Mypair._Myval2._Mylast - _Mypair._Myval2._Myfirst)" />
<Intrinsic Name="capacity" Expression="(size_t)(_Mypair._Myval2._Myend - _Mypair._Myval2._Myfirst)" />
<DisplayString>{{ size={size()} }}</DisplayString>
<Expand>
<Item Name="[capacity]" ExcludeView="simple">capacity()</Item>
<Item Name="[allocator]" ExcludeView="simple">_Mypair</Item>
<ArrayItems>
<Size>size()</Size>
<ValuePointer>_Mypair._Myval2._Myfirst</ValuePointer>
</ArrayItems>
</Expand>
</Type>
HResult 元素
HResult
元素可讓您自定義除錯程式視窗中 HRESULT 顯示的資訊。
HRValue
元素必須包含要自定義的 HRESULT 32 位值。
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
屬性群組會識別UIVisualizer
。ServiceId
是可視化檢視套件所公開之服務的 GUID。Id
是用於區分可視化檢視的唯一標識符,如果服務提供多個的話。 在上述範例中,相同的可視化檢視服務會提供兩個可視化檢視。MenuName
屬性會定義可視化檢視名稱,以顯示在調試程式中放大鏡圖示旁邊的下拉式清單中。 例如:
每個在 .natvis 檔案中定義的類型,都必須明確列出任何可以顯示它的使用者介面視覺器。 偵錯工具會比對類型條目中的與視覺化工具相關的參考與已註冊的視覺化工具。 例如,下列 std::vector
型別項目會參考上述範例中的 UIVisualizer
。
<Type Name="std::vector<int,*>">
<UIVisualizer ServiceId="{5452AFEA-3DF6-46BB-9177-C0B08F318025}" Id="1" />
</Type>
您可以在用來檢視記憶體中點陣圖的 Image Watch 延伸模組中看到 UIVisualizer
範例。
CustomVisualizer 元素
CustomVisualizer
是一個擴充點,指定您在Visual Studio Code 中撰寫以控制視覺效果的 VSIX 延伸模組。 如需撰寫 VSIX 延伸模組的詳細資訊,請參閱 Visual Studio SDK。
撰寫自定義的可視化檢視器比編寫 XML Natvis 定義要花費更多的精力,但這讓您不再受限於 Natvis 的功能支援。 自定義可視化檢視可以存取一組完整的調試程序擴充性 API,其可以查詢和修改偵錯程式,或與 Visual Studio 的其他部分通訊。
您可以在 CustomVisualizer
元素上使用 Condition
、IncludeView
和 ExcludeView
屬性。
局限性
Natvis 自定義會使用類別和結構,但不適用於 typedefs。
Natvis 不支援基本類型的可視化檢視(例如,int
、bool
),或基本類型的指標。 在此案例中,有一個選項是使用 格式規範, 適合您的使用案例。 例如,如果您在程式代碼中使用 double* mydoublearray
,則可以在調試程式的 Watch 視窗中使用陣列格式規範,例如運算式 mydoublearray, [100]
,其中顯示前 100 個元素。