[C++][调试技巧] VS 中的 Debugger Visualizers 使用指南 (.natvis文件)


简介

Debugger Visualizers 是微软在 Visual Studio 2013之后添加的功能.
可让程序员使用XMAL定义调试视窗的数据结构显示.
以更爽的方式来显示.
Visual Studio 中STL容器的调试显示信息就是通过该功能实现的.
注意!只支持结构体和类
下面是示例图:
STL容器的Visualizers演示
自定义数据视图支持中文显示.
当然,你也可以按照STL的格式来写,完全随你的心情


使用场合

我相信大家写数据结构的时候,经常感觉在Debug的时候,那个数据视窗,很别扭.
就没有STL那种简洁明了的感觉吧?

有没有既视感?
为了效率,写了个二级指针,结果让编译器的调试数据视窗丢失了边界,根本看不能一次性的看到所有数据.
必须要去监视视窗挨个this->map[x]来去看.
麻烦的要死.
这时候,就得使用Magic了.
老爹说过,要用魔法打败魔法.
在这里插入图片描述
更改之后的效果.
全部的数据都能看见了
现在的感觉就是爽!但又不太爽.
因为这些链表的显示,还是默认的,这怎么能容忍这么乱的信息?
在这里插入图片描述
链表类改造后的效果.
属实是爽了嗷.


环境设置

使用前的一些检查

1.首先,你需要确保你的编译器,不是Mixed模式.
八宝咸鱼
右键项目->属性页->配置属性->调试器->调试模式->选择非混合模式(Managed会不进入调试模式)
一般情况下,Visual Studio 会默认自动模式,图片中,我选择的是Native Only.
如果你不清楚这是什么模式,我不建议你跟我选择一样的.

2.不要使用本机兼容模式,和混合兼容模式.
步骤一
工具->选项->调试器->一般.
到了一般一面,确保你没有勾选托管兼容性模式
在这里插入图片描述


如何使用Visualizers(让.natvis文件生效)

1.添加.natvis文件
右键项目->添加新建项目->单元(也可能叫实用工具) 选择.natvis文件即可.

.natvis
2.在目录栏里,选择如下两个路径.
1.%VSINSTALLDIR%\Common7\Packages\Debugger\Visualizers
- STL的可视化文件就在这个目录下(stl.nativs),可以照着STL的文件学习一下
- 要求管理员权限
2.%USERPROFILE%\Documents\Visual Studio XXXX(你用的是什么版本就写什么)\Visualizers
- 如果该目录下,没有Visualizers文件夹,请自行添加.

诊断Natvis错误

可以使用Natvis诊断程序对语法进行故障排除并分析错误。当调试器在可视化条目中遇到错误时,它会忽略错误并以原始形式显示类型或选择其他合适的可视化。要了解为什么忽略某个可视化条目并查看潜在的错误,可以打开Natvis诊断工具/选项/调试/输出窗口/ Natvis诊断消息(仅C ++)选项。错误显示在“输出”窗口中

以上就全部设置完成了.
你已经可以正常的去配置你自己专属的调试器可视化数据了.


快速上手实例

例子很少,但是快速上手还是足够的

namespace bbxy
{
    using BOOL = int;

    template <typename dataType, size_t sizeMax>
    class testClass
    {
        dataType data[sizeMax]{ NULL };
        size_t maxSize{ sizeMax - 1 };

        void Traverse(const dataType data[])
        {
            for (size_t i = 0; i <= maxSize; i++)
                this->data[i] = data[i];
        }

    public:
        testClass(const dataType data[])
        {
            Traverse(data);
        }
    };
}

这是该类默认情况下的数据视图.

我们开写natvis文件

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

  <Type Name="bbxy::testClass&lt;*&gt;">
    <!--数据结构名 &lt;是< &gt;是> *则表示,接受任何-->
    <!--这段代码的意思:数据结构XXX,是个模板数据结构,任何模板参数都在其中-->
    <DisplayString>  {{ 元素个数: {maxSize+1}}} </DisplayString>
    <!--显示字符串,中间的空间内,你可以按照这个格式随便定义字符,maxSize是这个数据结构内的成员,这么写是合法的-->
    <Expand>
      <!--扩展 就是点开小三角后,出现的下来菜单-->
      <Item Name="[元素个数]">maxSize+1</Item>
      <!--这个就是第一个显示的子项-->
      <ArrayItems>
        <!--以数组的方式显示-->
        <Size>maxSize+1</Size>
        <!--这个数组应该有多少个-->
        <ValuePointer>data</ValuePointer>
        <!--这个数组的首地址应该是什么-->
      </ArrayItems>
    </Expand>
  </Type>

</AutoVisualizer>

下面的就是修改完之后的成品了
这就是成品
这个东西支持热修改,在.natvis文件修改之后,保存就会立刻有效果.


连续空间结构

数组

namespace bbxy
{
    using BOOL = int;

    template <typename dataType, size_t sizeMax>
    class Array
    {
        dataType data[sizeMax]{ NULL };
        size_t maxSize{ sizeMax };

        void Traverse(const dataType data[])
        {
            for (size_t i = 0; i <= maxSize; i++)
                this->data[i] = data[i];
        }

    public:
        Array(const dataType data[])
        {
            Traverse(data);
        }
    };
}

int main(int, char**, char**)
{
    bbxy::Array<int, 5>test{ new int[5]{1,2,3,4,5} };
    return 0;
}

对应的(.natvis文件)Xmal代码

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

  <Type Name="bbxy::Array&lt;*&gt;">
    <DisplayString>  {{ Element Count: {maxSize}}} </DisplayString>
    <Expand>
      <Item Name="[element count]">maxSize</Item>
      <ArrayItems>
        <Size>maxSize</Size>
        <ValuePointer>data</ValuePointer>
      </ArrayItems>
    </Expand>
  </Type>

</AutoVisualizer>

–实例中用过的语句–

<Type Name = "指定一个你要自定义数据视窗的数据类型"></Type>
<DisplayString>{{这里面的东西会被显示在视窗的Value(值)一栏}}</DisplayString>
<!--这是注释|{}单对花括号表示引用对象的值{{XXX:{LLL}Hello}}的效果就是 xxx:lllHello-->
<Expand>这个对应你展开数据结构的块.就是点击变量名的那个小三角所展开的内容,就是这里的</Expand>
<Item name = "这个是你想在名称这一栏中所显示的字符串">这里是你想要显示的值,注意不要使用引用的花括号</Item>
<!--Item块应该在Expand块内使用-->
<ArrayItems></ArrayItems>
<!--这个表示要有一个数组这样的连续内存结构-->
<Size>数组的大小,这里不要使用引用的花括号</Size>
<ValuePoint>数组的首地址</ValuePoint>

<!--数组结构的块例子-->
<ArrayItems>
<Size>大小</Size>
<ValuePoint>数组首地址</ValuePoint>
</ArrayItems>

如果你的数组是个模板数组,请使用($Txxx*)xxx
比如 template <int,double>
($T1*)就是int($T2*)就是double

效果图
在这里插入图片描述


非连续空间数据结构

链表

#include <iostream>

#ifndef NULL
#define NULL 0x0
#endif

namespace bbxy
{
    using BOOL = int;
    using uint = unsigned int;

    template <typename dataType>
    class Node
    {
    protected:
        dataType data;
    public:
        Node(const dataType& data):data(data)
        {
            
        }

        dataType& GetData(void)
        {
            return this->data;
        }
    };

    template <typename dataType>
    class LinkNode:public Node<dataType>
    {
        LinkNode* pNext;
        LinkNode* pBack;
    public:
        explicit LinkNode(const dataType& data = NULL,LinkNode* pNext = nullptr,LinkNode*pBack = nullptr) :Node<dataType>(data),pNext(pNext),pBack(pBack)
        {
        }

        dataType&GetData(void)
        {
            return Node<dataType>::data;
        }

        LinkNode*& GetNP()
        {
            return this->pNext;
        }
        LinkNode*& GetBP()
        {
            return this->pBack;
        }
    };

    template <typename dataType>
    class LinkList
    {
        LinkNode<dataType>* head;
        LinkNode<dataType>* tail;
        uint count;
    public:
        LinkList(const dataType& data = NULL, LinkNode<dataType>* pNext = nullptr, LinkNode<dataType>* pBack = nullptr)
            :head{ new LinkNode<dataType>{data,pNext,pBack} }, tail(head), count(1)
        {
            
        }

        void Push(const dataType& data = NULL,LinkNode<dataType>* pNext = nullptr, LinkNode<dataType>* pBack = nullptr)
        {
            LinkNode<dataType>* tmp = head;
            tail = tmp;
            while (tmp->GetNP())
            {
                tmp = tmp->GetNP();
            }
            tmp->GetNP() = new LinkNode<dataType>{ data,nullptr,tail };
            tmp->GetNP()->GetBP() = tmp;
            tmp = tmp->GetNP();
            tail = tmp;
            count++;
        }

        void Push(const dataType* arr = nullptr, uint arrBorder = NULL,LinkNode<dataType>* pNext = nullptr, LinkNode<dataType>* pBack = nullptr)
        {
            for (uint i = 0; i < arrBorder; i++)
                Push(*arr++);
        }
    };



    
}

int main(int, char**, char**)
{

    bbxy::LinkNode<int>test{ 0,new bbxy::LinkNode<int>{5} };
    bbxy::LinkList<int>link_list{};
    int arr[3]{ 99,98,97 };
    link_list.Push(arr,3);

    return 0;
}

  <?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010"><Type Name="bbxy::LinkNode&lt;*&gt;">
    <DisplayString>{{Data:{data} }}</DisplayString>
    <Expand>
      <Item Name="NextPoint">pNext</Item>
      <Item Name="BackPoint">pBack</Item>
    </Expand>
  </Type>

  <Type Name="bbxy::LinkList&lt;*&gt;">
    <DisplayString Condition="count > 0">{{Node Count:{count}}}</DisplayString>
    <Expand>
      <Item Condition="head" Name="Head">head</Item>
      <Item Condition="tail" Name="Tail">tail</Item>
      <LinkedListItems Condition="count > 0">
        <HeadPointer>head</HeadPointer>
        <NextPointer>pNext</NextPointer>
        <ValueNode>data</ValueNode>
      </LinkedListItems>
    </Expand>
  </Type>
  </AutoVisualizer>

大家可以看到,这个的语句和数组的非常相似
无非是ArrayItems变成了LinkListItems
数组首地址变成了头结点和链接指针

在这里插入图片描述
应用之后的效果


语法

所有语法相关内容,均来自微软官方的Visualizer帮助文档.
个人仅完成翻译和校对,对部分重要文本进行加粗突出显示
如对翻译不喜,可自行去官方文档翻阅:官方文档链接


自动数据视窗元素(AutoVisualizer)

AutoVisualizer 元素是.natvis文件的根节点,并且包含的命名空间xmlns:属性

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

类型元素(Type)

基本类型如下所示:

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

它指定:

1.此可视化应用于什么类型(Type Name属性)。

2.该类型的对象(DisplayString元素)的值应为什么样。

3.当用户在变量窗口(Expand节点)中将其展开时,该类型的成员应具有什么样的外观, 模板化类元素的Name属性Type接受一个星号*作为通配符,可用于模板化类名:

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

在此示例中,无论对象是CAtlArray<int>还是CAtlArray<float>,都将使用相同的可视化效果。如果CAtlArray有一个特定的可视化条目,那么它将优先于该泛型条目。
请注意,可以使用宏$ T1,$ T2等在可视化条目中引用模板参数。要查找这些宏的示例,请参阅 Visual Studio 附带的 .natvis 文件


可视化器类型匹配

如果可视化条目无法验证,则使用下一个可用的可视化


继承属性(Inheritable)

可以使用可选Inheritable属性指定可视化仅适用于基本类型还是适用于基本类型以及所有派生类型。在下面的视图中,可视化仅适用于BaseClass类型:

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

Inheritable默认是true


优先级属性(Priority)

该Priority属性指定在定义解析失败时使用替代定义的顺序。可能的值Priority是:Low,MediumLow,Medium,MediumHigh,和High,默认值是Medium。

优先级属性仅应用于区分同一.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>

版本元素(Version)

使用该Version元素可以将可视化范围限定于特定模块及其版本,以便可以将名称冲突最小化,并且可以将不同的可视化用于不同类型的类型。例如:

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

在此示例中,可视化仅适用DirectUI::Border于Windows.UI.Xaml.dll1.0 到 1.5 版本中的类型。请注意,添加版本元素将可视化条目的作用域限定于特定的模块和版本,并减少了意外的不匹配,但是如果在不同模块使用的通用头文件中定义了类型,则当不在该类型中时,将不应用版本化的可视化指定的模块。


可选属性(Optional)

Optional 属性可以出现在任何节点上。如果可选节点内的任何子表达式无法解析,则将忽略该节点,但是Type元素的其余部分仍然有效。在以下类型中,[state]是非可选的,但是[exception]是可选的。这意味着如果 MyNamespace :: MyClass 包含一个名为 _M_exceptionHolder 的字段,您仍然会同时看到 [state] 节点和 [exception] night,但是如果 _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,则不应用规则指定的可视化元素。如果计算结果为真,或者没有条件属性,则可视化规则应用于该类型。您可以将此属性用于可视化条目中的 if-else 逻辑。例如,下面的可视化有两个用于智能指针类型的 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>

_Myptr当成员为null时,第一个DisplayString元素的条件解析为true,从而显示该表单。 _Myptr当成员不为null时,条件的计算结果为false,并显示第二个元素DisplayString。


包含视图属性和排除视图属性(IncludeView & ExcludeView)

这些属性指定要在不同视图中显示或不显示的元素。例如,给定natvre的std :: vector规范;

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

在简单视图中不显示[size]和[capacity]项目。如果我们使用IncludeView =“ simple”而不是ExcludeView,则[size]和[capacity]项目将显示在简单视图中,而不是默认视图中。
您可以在类型以及单个成员上使用IncludeView和ExcludeView属性。


字符串显示元素(DisplayString)

DisplayString元素指定作为变量值显示的字符串。它接受混合了表达式的任意字符串。花括号内的所有内容都被解释为表达式。例如,一个像这样的DisplayString条目:

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


在 DisplayString 表达式中,作为 CPoint 成员的 x 和 y 位于花括号内,因此它们的值被计算。 该表达式还显示了如何使用双花括号( {{ 或 }} )转义花括号。

备注
DisplayString 元素是唯一接受任意字符串和花括号语法的元素。所有其他可视化元素仅接受由调试器评估的表达式


字符串视图元素(StringView)

StringView 元素定义了其值将被发送到内置文本可视化器的表达式。例如,假设我们对 ATL::CStringT 类型有以下可视化:

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

CStringT 对象看起来像:
在这里插入图片描述
可视化在一个变量窗口中显示一个 CStringT 对象,如下所示:

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

添加 StringView 元素将向调试器指示该值可以通过文本可视化查看:
请注意下面值旁边显示的放大镜图标。选择图标将启动文本可视化器,它将显示 m_pszData 指向的字符串。
在这里插入图片描述

备注
请注意,表达式 {m_pszData,su} 包含 C++ 格式说明符su 以将值显示为 Unicode 字符串。有关详细信息,请参阅 C++ 中的格式说明符。


扩展元素(Expand)

当用户在变量窗口中展开该节点时,Expand节点用于自定义可视化类型的子节点。它接受定义子元素的子节点列表。
Expand节点是可选的。

  • 如果未在可视化条目中指定Expand节点,则将使用Visual Studio的默认展开规则。
  • 如果指定的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 类型将如下所示:
在这里插入图片描述
宽度和高度元素中指定的表达式被计算并显示在值列中。 每当使用自定义扩展时,调试器都会自动创建[Raw View]节点。 它在上面的屏幕截图中进行了扩展,以显示对象的原始视图与其可视化的不同之处。 Visual Studio 默认扩展为基类创建一个子树,并将基类的所有数据成员列为子类。

备注
如果 item 元素的表达式指向复杂类型,则 Item 节点本身是可扩展的。

数组扩展项(ArrayItem)


使用 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会显示其各个元素:
在这里插入图片描述
ArrayItems 节点至少必须具有:

  1. 用于调试器了解数组长度的大小表达式(必须计算为整数)
  2. 一个 ValuePointer 表达式,它应该指向第一个元素(它必须是一个不是 void* 的元素类型的指针)。

数组下界的默认值为0。可以使用LowerBound元素覆盖该值(可以在Visual Studio附带的.natvis文件中找到示例)。

您现在可以将 [] 运算符与 ArrayItems 扩展一起使用,例如 vector[i]。 [] 运算符可用于使用 ArrayItems 或 IndexListItems 的一维数组的任何可视化,即使类型本身不允许此运算符(例如 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>  
    </ArrayItems>  
  </Expand>  
</Type>

Forward指定数组是行优先还是列优先。 Rank 指定数组的等级。 Size 元素接受隐式 $i 参数,它用维度索引替换该参数以查找该维度中数组的长度。 例如,在前面的示例中,在表达式_M_extent.M_base [0]上方应将第0维的长度指定为_M_extent._M_base [1],以此类推。
下面是一个二维 Concurrency::array 对象在调试器中的显示:
在这里插入图片描述

索引链表扩展项(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>

您现在可以将 [] 运算符与 IndexListItems 扩展一起使用,例如 vector[i]。 [] 运算符可用于使用 ArrayItems 或 IndexListItems 的一维数组的任何可视化,即使类型本身不允许此运算符(例如 CATLArray)。

ArrayItems 和 IndexListItems 之间的唯一区别是 ValueNode 期望带有隐式 $i 参数的第 i 个元素的完整表达式。


链表子扩展项(Linked ListItems)

如果可视化类型表示链表,则调试器可以使用 LinkedListItems 节点显示其子项。这是使用此功能的 CAtlList 类型的可视化:

<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 指向项的值。

  • NextPointer 和 ValueNode 表达式在链表节点元素的上下文中计算,而不是在父列表类型中计算。 在上面的例子中, CAtlList 有一个 CNode 类(在 atlcoll.h 中找到),它代表链表的一个节点。 m_pNext 和 m_element 是该 CNode 类的字段,而不是 CAtlList 类的字段。
  • ValueNode 可以留空或使用 this 来引用链表节点本身。

自定义列表扩展项(Custom List Items)

CustomListItems 扩展允许您编写用于遍历数据结构(例如哈希表)的自定义逻辑。 您应该使用CustomListItems来可视化数据结构,其中需要评估的所有内容都可以通过C ++表达式表示,但并不完全适合ArrayItems,TreeItems或LinkedListItems。
CAtlMap 的可视化工具是 CustomListItems 适合的一个很好的例子。

<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 节点显示其子项。这是使用此功能的 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 节点非常相似。在树节点类的上下文中评估LeftPointer,RightPointer和ValueNode,并且ValueNode可以保留为空,或使其引用树节点本身。


子扩展项(ExpandedItem)

扩展项元素可用于生成聚合的子视图,方法是将基类或数据元素的属性显示为可视化类型的子元素。指定的表达式将被求值,子节点将被进一步查看到可视化类型的子元素。 中。,假想我们有一个智能指针,例如auto_ptr<vector>,它通常为:
在这里插入图片描述
要查看向量的值,您必须在通过 _Myptr 成员的变量窗口中向取两个级别。 通过添加 ExpandedItem 元素,您可以从层次结构中消除 _Myptr 变量并直接查看向量元素:

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

在这里插入图片描述
下面的示例显示了如何从派生类中的基类聚合属性。 假设 CPanel 类派生自 CFrameworkElement。 ExpandedItem 节点允许将这些属性附加到 CPanel 类的子列表中,而不是重复来自基本 CFrameworkElement 类的属性。 这里需要关闭派生类的可视化匹配的 nd 格式说明符。 否则,表达式 (CFrameworkElement)this 将导致再次应用 CPanel 可视化,因为默认可视化类型匹配规则认为它是最合适的。 如果基类没有可视化,则使用 nd 格式说明符指示调试器使用基类可视化或基类默认扩展。

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

聚合扩展项(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>

在这里插入图片描述


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>

UIVisualizer 由 ServiceId - Id 属性对标识。 ServiceId 是 Visualizer 包公开的服务的 GUID,Id 是唯一标识符,如果服务提供多个可视化器,则可用于区分可视化器。 在上面的示例中,同一个可视化器服务提供了两个可视化器。

MenuName 属性是用户在调试器变量窗口中打开放大镜图标旁边的下拉菜单时看到的可视化工具的名称,例如:
在这里插入图片描述
.natvis 文件中定义的每种类型都必须明确列出可以显示它们的 UI 可视化工具。 调试器匹配类型条目中的可视化器引用,以将类型与注册的可视化器相匹配。 例如,以下示例中std :: vector的类型条目引用了UIVisualizer。

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

您可以在用于查看内存位图的 Image Watch 扩展中看到 UIVisualizer 的示例:ImageWatch


自定义可视化元素(CustomVisualizer)

CustomVisualizer 是一个扩展点,它指定一个 VSIX 扩展,您可以编写该扩展来控制在 Visual Studio 中运行的代码中的可视化。 有关编写 VSIX 扩展的详细信息,请参阅 Visual Studio SDK。 编写自定义可视化工具比编写 XML natvis 定义要多得多,但您不受 natvis 支持或不支持的限制。 自定义可视化工具可以访问全套调试器可扩展性 API,这些 API 可用于查询和修改被调试对象进程或与 Visual Studio 的其他部分进行通信。

您可以在 CustomVisualizer 元素上使用 Condition、IncludeView 和 ExcludeView 属性。


  • 6
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

八宝咸鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值