Introduction

Visual Studio 2012 introduces a new type visualization framework (natvis) for customizing the way C++ types are displayed in debugger variable windows (watch, locals, data tips etc.). It replaces the autoexp.dat file that has been used in earlier versions of Visual Studio, and offers xml syntax, better diagnostics, versioning and multiple file support. You can use this framework to enhance debugger's view of your custom data types. This sample page contains syntax reference and examples for visualizer xml elements, instructions for turning on diagnostics, and a simple project that demonstrates VSIX deployment of type visualizers.

 

Building the Sample

You need to have Visual Studio 2012 RC SDK in order to open and build the project in this sample. You can download it from http://www.microsoft.com/en-us/download/details.aspx?id=29930. Note that this is necessary only if you are interested in learning more about VSIX deployment of visualizers. You can always manually copy the visualizer files to your Visual Studio installation. This is explained further below.

 

Description

Natvis files

Type visualizers for C++ types are specified in .natvis files. A natvis file is simply an xml file (with a .natvis extension) with its schema defined in <VSINSTALLDIR>\Xml\Schemas\natvis.xsd. A natvis file contains visualization rules for one or more types. Visual Studio ships with a few natvis files in <VSINSTALLDIR>\Common7\Packages\Debugger\Visualizers folder. These files contain visualization rules for many common types and can serve as examples when writing visualizers for new types.

The basic structure of a natvis file is as follows, where each 'Type' element represents a visualizer entry for a type whose fully qualified name is specified in the 'Name' attribute.

XML
Edit|Remove
<?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>
You can start writing a visualizer for your types by creating a natvis file having the above structure and dropping it into one of the locations below:

At the start of each debugging session, Visual Studio will load and process every natvis file it can find in these locations (it is NOT necessary to restart Visual Studio). This makes writing new visualizers easy as you can stop debugging, make changes to your visualizer entries, save the natvis file and start debugging again to see the effects of your changes.

Natvis diagnostics

Natvis diagnostics is a very important tool that helps troubleshooting issues when writing new type visualizers. When the debugger encounters errors in a visualizer entry (e.g. xml schema errors, expression fails to parse), it will simply ignore it and display the type in its raw form or pick another suitable visualizer. To understand why a certain visualizer entry is ignored and to see what the underlying errors are, you can turn on visualization diagnostics which is controlled by the following registry value:

[HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\11.0_Config\Debugger]

"EnableNatvisDiagnostics"=dword:00000001

When turned on, you will see diagnostic status messages (e.g. when a natvis file is parsed, an expression is successfully evaluated) and error messages (e.g. file parse errors, expression parse errors) in the output window in Visual Studio.

Syntax Reference

The structure of a basic visualizer entry looks like:

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

Most of the different xml elements that can be used to define type visualizers are explained in this section with examples.

Condition attribute

The optional condition attribute is available for many visualizer elements and specifies when a visualization rule should be used. If the expression inside the condition attribute is false, then the visualization rule specified by the element is not applied. If it’s evaluated to true, or if there is no condition attribute then the visualization rule is applied to the type. You can use this attribute to have if-else logic in the visualization entries. For instance, the visualizer below defines two DisplayString elements for a smart pointer type:

XML
Edit|Remove
<Type Name="std::auto_ptr&lt;*&gt;"  <DisplayString Condition="_Myptr == 0">empty</DisplayString> 
  <DisplayString>auto_ptr {*_Myptr}</DisplayString> 
  <Expand    <ExpandedItem>_Myptr</ExpandedItem> 
  </Expand> 
</Type>
When _Myptr member is null, the condition of the first DisplayString element will be true, therefore that element takes effect. When the _Myptr member is not null, the condition evaluates to false and the second DisplayString element takes effect.

DisplayString

DisplayString node specifies the string to be shown as the value of the variable. It accepts arbitrary strings mixed with expressions. Everything inside curly braces is interpreted as an expression and gets evaluated. For instance:

XML
Edit|Remove
<Type Name="CPoint"  <DisplayString>{{x={x} y={y}}}</DisplayString> 
</Type>
 will result in variables of type CPoint to look like:

Here, x and y, which are members of CPoint, are inside curly braces and their values are evaluated. Note that the example also shows you can escape a curly brace by using double curly braces (i.e. {{ or }}).

One important point to remember is DisplayString element is the only element that accepts arbitrary strings and the curly brace syntax. All other visualization elements accept only expressions that are evaluated by the debugger.

StringView

Adding a StringView element tells the debugger that this value can be viewed by a text visualizer:

XML
Edit|Remove
<Type Name="ATL::CStringT&lt;wchar_t,*&gt;"  <DisplayString>{m_pszData,su}</DisplayString> 
  <StringView>m_pszData,su</StringView> 
</Type>
Notice the glass icon shown next to the value below. Clicking the icon will launch the text visualizer which will display the string that m_pszData points to.

Expand

Expand node is used to customize the children of the visualized type when the user expands it in the variable windows. It accepts a list of child nodes, which in turn define the child elements. It is important to know that Expand node is optional and if no Expand is specified in a visualizer entry, Visual Studio’s default expansion rules will be used. If an expand node is specified with no child nodes under it, then the type won’t be expandable in the debugger windows (i.e. no plus sign next to the variable name).

Item Expansion

The Item node, which is the most basic and most common node to be used under an Expand node, defines a single child element. For instance, if you have a CRect class with top, left, right, bottom as its fields and the following visualizer entry

XML
Edit|Remove
<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>
then CRect type is going to look like below

The expressions specified in Width and Height elements are evaluated and shown in the value column. An additional point to remember is if the expression of the item element points to a complex type then the Item node itself will be expandable.

ArrayItems Expansion

ArrayItems node can be used to have the debugger interpret the type as an array and display its individual elements. The visualizer for std::vector is a good example using this node:

XML
Edit|Remove
<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>
A std::vector shows its individual elements when expanded in the variable window:

At a minimum, the ArrayItems node must have the 'Size' expression (which must evaluate to an integer) for the debugger to understand the length of the array and the 'ValuePointer' expression that should point to the first element (which must be a pointer of the element type that is not void*). The array lower bound is assumed to be 0 which can be overridden by using 'LowerBound' node (examples of this can be found in the default natvis files shipped with Visual Studio).

Multi-dimensional arrays can also be specified. The debugger needs just a little bit more information to properly display child elements in that case:

XML
Edit|Remove
<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>
'Direction' specifies whether the array is row-major or column-major order. 'Rank' specifies the rank of the array. 'Size' element accepts the implicit '$i' parameter which it substitutes with dimension index to find the length of the array in that dimension. For instance, in the example above the expression _M_extent.M_base[0] should give the length of the 0th dimension, _M_extent._M_base[1] the 1st and so on.

IndexListItems Expansion

ArrayItems assume array elements are laid out contiguously in memory. Debugger gets to the next element by simply incrementing its pointer to the current element. To support cases where you need to manipulate the index to the value node, index list items can be used. Here’s a visualizer using 'IndexListItems' node:

XML
Edit|Remove
<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>
The only difference between ArrayItems and IndexListItems is that the 'ValueNode' expects the full expression to the ith  element with the implicit '$i' parameter.

LinkedListItems Expansion

If the visualized type represents a linked list, debugger can be instructed to display its children via 'LinkedListItems' node. Here’s the visualizer for the CAtlList type using this node:

XML
Edit|Remove
<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' expression refers to the length of the list, 'HeadPointer' points to the first element, 'NextPointer' refers to the next element, and 'ValueNode' refers to the value of the item. Two important points to remember with LinkedListItems node are:
  1. 'NextPointer' and 'ValueNode' expressions are evaluated under the context of the linked list node element and not the parent list type. In the example above, CAtlList has a CNode class (can be found in atlcoll.h) that represents a node of the linked list. m_pNext and m_element are fields of that CNode class and not of CAtlList class. 
  2. 'ValueNode' can be left empty or have 'this' to refer to the linked list node itself.

TreeItems Expansion

If the visualized type represents a tree, debugger can walk the tree and display its children via 'TreeItems' node. Here’s the visualizer for the std::map type using this expansion:

XML
Edit|Remove
<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-&gt;_Parent</HeadPointer> 
      <LeftPointer>_Left</LeftPointer> 
      <RightPointer>_Right</RightPointer> 
      <ValueNode Condition="!((bool)_Isnil)">_Myval</ValueNode> 
    </TreeItems> 
  </Expand> 
</Type>
The syntax is very similar to LinkedListItems node. 'LeftPointer', 'RightPointer', 'ValueNode' are evaluated under the context of the tree node class, and 'ValueNode' can be left empty or have “this” to refer to the tree node itself.

ExpandedItem Expansion

ExpandedItem can be used to generate an aggregated children view by having properties coming from base classes or data members displayed as if they were children of the visualized type. The specified expression is evaluated and the child nodes of the result are appended to the children list of the visualized type. For instance, suppose we have a smart pointer type auto_ptr<vector<int>> which will normally be displayed as:

To see the values of the vector, we need to drill down two levels in the variable window passing through _Myptr member. Adding a visualizer entry using ExpandedItem element:

XML
Edit|Remove
<Type Name="std::auto_ptr&lt;*&gt;"  <DisplayString>auto_ptr {*_Myptr}</DisplayString> 
  <Expand    <ExpandedItem>_Myptr</ExpandedItem> 
  </Expand> 
</Type>
we can eliminate _Myptr variable from the hierarchy and directly view vector elements:

Synthetic Item Expansion

'Synthetic' node allows one to create an artificial child element (i.e. one that is not a result of an expression) which might contain children elements on its own. The example below shows two such nodes which is, it is used to create an artificial node:

XML
Edit|Remove
<Type Name="Rectangle" 
  <Expand 
    <Synthetic Name="Am I a square?" Condition="height==width" 
      <DisplayString>Yes I am. </DisplayString> 
    </Synthetic>  
    <Synthetic Name="Am I a square?" Condition="height!=width" 
      <DisplayString>No I am not.</DisplayString> 
    </Synthetic>  
  </Expand>  
</Type> 

Visualizer – Type Matching

Here are the general rules governing how visualizers are matched with types to be viewed in the debugger windows:

Versioning

Visualizers can be scoped to specific modules and their versions so that name collisions can be minimized and different visualizers can be used for different versions of the types. The optional version element is used to specify this. For instance:

XML
Edit|Remove
  <Type Name="DirectUI::Border"    <Version Name="Windows.UI.Xaml.dll" Min="1.0" Max="1.5"/> 
    <DisplayString>{{Name = {*(m_pDO-&gt;m_pstrName)}}}</DisplayString> 
    <Expand      <ExpandedItem>*(CBorder*)(m_pDO)</ExpandedItem>    </Expand> 
  </Type>
Here, the visualizer is only applicable for the DirectUI::Border type found in the Windows.UI.Xaml.dll from version 1.0 to 1.5. Note that while adding version elements will scope visualizer entry to a particular module / version and reduce inadvertent matches, if a type is defined in a common header file that is used by different modules, it will prevent the visualizer from taking effect when the type is not in the specified module.

VSIX deployment

You can always manually copy natvis files to one of the predetermined locations for the debugger to discover and load them but you can make it easier for the developers using your visualizers to deploy them via VSIX. 

The attached sample project shows how you can do this which is actually pretty simple and straightforward. Once you create your natvis file and add it to your project, you need to

  1. Include it in VSIX by going to the property tab for the natvis file and set 'Include in VSIX' attribute to 'True'.
  2.  Declare it as a NativeVisualizer asset in the vsixmanifest:
XML
Edit|Remove
<PackageManifest ...  ... 
  <Assets    <Asset Type="NativeVisualizer" Path="vector.natvis"/> 
  </Assets> 
</PackageManifest>
Once you build the attached project and install it, you can try viewing variables of type std::vector<int>. You should see 'CUSTOM VISUALIZER: ...' in the value column for them.