Visual Studio调试器指南---Natvis 语法参考
AutoVisualizer 元素
AutoVisualizer
元素是 .natvis 文件的根节点,并包含命名空间 xmlns:
属性。
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
.
.
</AutoVisualizer>
AutoVisualizer
元素可以具有类型、 HResult、 UIVisualizer和CustomVisualizer子项。
Type 元素
基本 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>
可以使用 $T1 和 $T2 这样的宏,在可视化条目中引用模板参数。 有关这些宏的示例,请参阅 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 匹配的条目。 如果分析失败,就会使用 STL 的 2013 版本的备用条目:
<!-- VC 2013 -->
<Type Name="std::reference_wrapper<*>" Priority="MediumLow">
<DisplayString>{_Callee}</DisplayString>
<Expand>
<ExpandedItem>_Callee</ExpandedItem>
</Expand>
</Type>
<!-- VC 2015 -->
<Type Name="std::reference_wrapper<*>">
<DisplayString>{*_Ptr}</DisplayString>
<Expand>
<Item Name="[ptr]">_Ptr</Item>
</Expand>
</Type>
可选特性
你可以将 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
属性用于指定是否在特定视图内显示元素。 例如,在下面的 Natvis std::vector
规范中,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
元素有助于避免名称冲突、减少无意间的不匹配,还允许在不同的类型版本中使用不同的可视化效果。
如果某个由不同模块使用的公共头文件定义了一个类型,则只有当该类型位于指定的模块版本中时,版本化的可视化效果才会显示。
在下面的示例中,可视化效果只适用于在 DirectUI::Border
版本 1.0 到 1.5 中找到的 Windows.UI.Xaml.dll
类型。
<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或some。 不允许使用路径名。
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
节点,子项将使用默认的展开规则。 -
如果指定的
Expand
节点下面没有子节点,类型就无法在调试器窗口中展开。
Item 展开
Item
元素是 Expand
节点中最基本、最常见的元素。 Item
定义单个子元素。 例如,一个包含 CRect
、top
、left
和 right
字段的 bottom
类具有以下可视化条目:
<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
元素中指定的表达式,然后在变量窗口的值列中显示值。
调试器会为每个自定义展开自动创建 [Raw View] 节点。 上面的屏幕截图中的 [Raw View] 节点是展开的,显示了对象的默认原始视图与其 Natvis 可视化效果的区别。 默认展开会为基类创建一个子树,并将基类的所有数据成员以子项的形式列出。
备注
如果项元素的表达式指向复杂类型,则项节点本身是可展开的。
Size
使用 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>
</ArrayItems>
</Expand>
</Type>
Direction
指定数组是采用行优先顺序还是列优先顺序。Rank
指定数组的秩。Size
元素接受将其替换为维索引以查找该维度中数组的长度的隐式$i
参数。 在前面的示例中,表达式_M_extent.M_base[0]
应为第0个维度指定长度,_M_extent._M_base[1]
为1,依此类推。
下面是调试器窗口中显示的一个二维 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
引用项的值。
调试器将在 NextPointer
节点元素(而不是父列表类型)环境中计算 ValueNode
和 LinkedListItems
表达式。 在前面的示例中,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
节点遍历该树并显示其子级。 下面是使用 TreeItems
节点的 std::map
类型的可视化效果:
<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 展开
通过将基类或数据成员的属性显示为可视化类型的子项,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
。 CFrameworkElement
节点的可视化会将基 ExpandedItem
类的属性附加到 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 Item 展开
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>
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 文件中定义的每种类型都必须明确列出能够显示它的所有 UI 可视化工具。 调试器会将类型条目中的可视化工具引用与注册的可视化工具相匹配。 例如,下面的 std::vector
类型条目引用的是前一个示例中的 UIVisualizer
。
<Type Name="std::vector<int,*>">
<UIVisualizer ServiceId="{5452AFEA-3DF6-46BB-9177-C0B08F318025}" Id="1" />
</Type>
你可以在用于查看内存中位图的图像监视扩展中查看 UIVisualizer
的示例。
CustomVisualizer 元素
CustomVisualizer
是一个扩展点,用于指定你编写的 VSIX 扩展,以便在 Visual Studio 代码中控制可视化效果。 有关编写 VSIX 扩展的更多信息,请参阅 Visual Studio SDK。
编写自定义可视化工具比 XML Natvis 定义要费事得多,但 Natvis 在支持方面的限制对你没有影响。 自定义可视化工具有权访问所有的调试器扩展性 API,因此可以查询和修改调试对象进程,也可以与 Visual Studio 的其他部分通信。
可以在 Condition
元素上使用 IncludeView
、ExcludeView
和 CustomVisualizer
属性。
限制
Natvis 自定义项适用于类和结构,但不能使用 typedef。
Natvis 不支持用于基元类型的可视化工具(例如 int
、bool
)或指向基元类型的指针。 在此方案中,一种选择是使用适合用例的格式说明符。 例如,如果您在代码中使用 double* mydoublearray
,则可以在调试器的 "监视" 窗口中使用数组格式说明符,如表达式 mydoublearray, [100]
,这将显示前100个元素。