VS2012 debugger type visualizers for ObjectARX

The C++ developers among you may remember the autoexp.dat file, which tells older versions of Visual Studio how to visualize custom C++ types during a debug session. Here's an ancient post showing how we extended it for some basic ObjectARX types and another showing how to do so via a custom plug-in.

In Visual Studio 2012, a newer XML-based mechanism was introduced to do something similar. In today's post we'll look at a custom .natvis file that exposes some basic ObjectARX types to the Visual Studio debugger.

This file was created by Davis Augustine in response to a query from Augusto Gonçalves. Augusto has also posted the file to GitHub – something we talked about in the last post – which will allow you all to contribute changes to the file, should you so wish.

For Visual Studio (2012 or later) to find the .natvis file, it has to be in one of these locations:

%VSINSTALLDIR%\Common7\Packages\Debugger\Visualizers

%USERPROFILE%\My Documents\Visual Studio 2012\Visualizers

[The first requires admin rights and the second should have the 2012 changed to the appropriate number for newer versions of VS, of course.]

Here's a recent version of the acad.natvis file, reformatted for this blog. This version supports AcString, AcArray, AcRxClass, AcString, CAdUiPathName, CAdUiVolumeDescriptor and resbuf, with future candidates being AcRxValue, AcDbObject, AcDbObjectId, AcGe*, etc. You'll continue to find the latest & greatest on GitHub, of course.

<?xml version="1.0" encoding="utf-8"?>

<AutoVisualizer

  xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">

 

  <!-- Version 1.0e 11nov14 -->

  <!-- for acad/arx types -->

 

  <Type Name="AcArray&lt;*&gt;">

    <DisplayString>

      {{Len = {mLogicalLen}}}

    </DisplayString>

    <Expand>

      <Item Name="Len">mLogicalLen</Item>

      <Item Name="Buf Siz">mPhysicalLen</Item>

      <ArrayItems>

        <Size>mLogicalLen</Size>

        <ValuePointer>mpArray</ValuePointer>

      </ArrayItems>

    </Expand>

  </Type>

 

  <Type Name="AcRxClass">

    <DisplayString Condition="m_pImp!=0">

      {*(((wchar_t **)m_pImp)+1),su}

    </DisplayString>

  </Type>

 

  <Type Name="AcString">

    <DisplayString Condition="mnFlags==0">""</DisplayString>

    <DisplayString Condition="mnFlags==1">{mszStr,s}</DisplayString>

    <DisplayString Condition="mnFlags==2">

      {mchr.mwszStr,su}

    </DisplayString>

    <DisplayString Condition="mnFlags==3">

      {mptr.mpszData,s}

    </DisplayString>

    <DisplayString Condition="mnFlags==4">

      {mptr.mpwszData,su}

    </DisplayString>

    <DisplayString Condition="mnFlags==5">

      {*(wchar_t **)(mptr.mpPtrAndData),su}

    </DisplayString>

    <StringView Condition="mnFlags==0">""</StringView>

    <StringView Condition="mnFlags==1">mszStr,s</StringView>

    <StringView Condition="mnFlags==2">mchr.mwszStr,su</StringView>

    <StringView Condition="mnFlags==3">mptr.mpszData,s</StringView>

    <StringView Condition="mnFlags==4">mptr.mpwszData,su</StringView>

    <StringView>mptr.mpPtrAndData</StringView>

  </Type>

 

  <Type Name="CAdUiPathname">

    <DisplayString Condition="m_pathbuffer!=0">

      {*m_pathbuffer,su}

    </DisplayString>

    <DisplayString Condition="m_this_type==0">

      NO_PATH

    </DisplayString>

    <StringView Condition="m_pathbuffer!=0">

      *m_pathbuffer

    </StringView>

  </Type>

 

  <Type Name="CAdUiVolumeDescriptor">

    <DisplayString Condition="m_vol_localname!=0">

      {*m_vol_localname}

    </DisplayString>

    <StringView Condition="m_vol_localname!=0">

      *m_vol_localname

    </StringView>

  </Type>

 

  <Type Name="resbuf">

    <!--ARX/Lisp Function arg-->

    <DisplayString Condition="restype==5000">

      rtnone

    </DisplayString>

    <DisplayString Condition="restype==5001">

      {resval.rreal} rreal</DisplayString>

    <DisplayString Condition="restype==5002">

      {resval.rpoint[0]},{resval.rpoint[1]}

    </DisplayString>

    <DisplayString Condition="restype==5003">

      {resval.rint} rint

    </DisplayString>

    <DisplayString Condition="restype==5004">

      {resval.rreal} rreal

    </DisplayString>

    <DisplayString Condition="restype==5005">

      {resval.rstring}

    </DisplayString>

    <DisplayString Condition="restype==5006">

      soft pointer id

    </DisplayString>

    <DisplayString Condition="restype==5007">

      pick set

    </DisplayString>

    <DisplayString Condition="restype==5008">

      orientation</DisplayString>

    <DisplayString Condition="restype==5009">

      {resval.rpoint[0]},{resval.rpoint[1]},{resval.rpoint[2]}

    </DisplayString>

    <DisplayString Condition="restype==5010">

      {resval.rlong} rlong

    </DisplayString>

    <DisplayString Condition="restype==5016">

      list-begin

    </DisplayString>

    <DisplayString Condition="restype==5017">

      list-end

    </DisplayString>

    <DisplayString Condition="restype==5018">

      dotted pair

    </DisplayString>

    <DisplayString Condition="restype==5031">

      {resval.mnInt64} int64

    </DisplayString>

 

    <!--DXF/XData String-->

    <DisplayString

      Condition="(restype&gt;=1) &amp;&amp; (restype&lt;=9)">

      {resval.rstring}

    </DisplayString>

    <DisplayString

      Condition="(restype&gt;=100) &amp;&amp; (restype&lt;=103)">

      {resval.rstring}

    </DisplayString>

    <DisplayString Condition="restype==105">

      {resval.rstring}

    </DisplayString>

    <DisplayString

      Condition="(restype&gt;=300) &amp;&amp; (restype&lt;=309)">

      {resval.rstring}

    </DisplayString>

    <DisplayString Condition="restype==410">

      {resval.rstring}

    </DisplayString>

    <DisplayString

      Condition="(restype&gt;=430) &amp;&amp; (restype&lt;=439)">

      {resval.rstring}

    </DisplayString>

    <DisplayString

&#
160;    
Condition="(restype&gt;=470) &amp;&amp; (restype&lt;=479)">

      {resval.rstring}

    </DisplayString>

    <DisplayString

      Condition="(restype&gt;=999) &amp;&amp; (restype&lt;=1003)">

      {resval.rstring}

    </DisplayString>

 

    <!--DXF/XData Double-->

    <DisplayString

      Condition="(restype&gt;=38) &amp;&amp; (restype&lt;=59)">

      {resval.rreal} rreal

    </DisplayString>

    <DisplayString

      Condition="(restype&gt;=140) &amp;&amp; (restype&lt;=149)">

      {resval.rreal} rreal

    </DisplayString>

    <DisplayString

      Condition="(restype&gt;=460) &amp;&amp; (restype&lt;=469)">

      {resval.rreal} rreal

    </DisplayString>

    <DisplayString

      Condition="(restype&gt;=1040) &amp;&amp; (restype&lt;=1042)">

      {resval.rreal} rreal

    </DisplayString>

 

    <!--DXF/XData Point-->

    <DisplayString

      Condition="(restype&gt;=10) &amp;&amp; (restype&lt;=17)">

      {resval.rpoint[0]},{resval.rpoint[1]},{resval.rpoint[2]}

    </DisplayString>

    <DisplayString

      Condition="(restype&gt;=110) &amp;&amp; (restype&lt;=112)">

      {resval.rpoint[0]},{resval.rpoint[1]},{resval.rpoint[2]}

    </DisplayString>

    <DisplayString

      Condition="(restype&gt;=210) &amp;&amp; (restype&lt;=219)">

 &#
160;    {resval.rpoint[0]},{resval.rpoint[1]},{resval.rpoint[2]}

    </DisplayString>

    <DisplayString

      Condition="(restype&gt;=1010) &amp;&amp; (restype&lt;=1013)">

      {resval.rpoint[0]},{resval.rpoint[1]},{resval.rpoint[2]}

    </DisplayString>

 

    <!--DXF/XData Int16-->

    <DisplayString

      Condition="(restype&gt;=60) &amp;&amp; (restype&lt;=79)">

      {resval.rint} rint

    </DisplayString>

    <DisplayString

      Condition="(restype&gt;=270) &amp;&amp; (restype&lt;=279)">

      {resval.rint} rint

    </DisplayString>

    <DisplayString

      Condition="(restype&gt;=370) &amp;&amp; (restype&lt;=389)">

      {resval.rint} rint

    </DisplayString>

    <DisplayString

      Condition="(restype&gt;=400) &amp;&amp; (restype&lt;=409)">

      {resval.rint} rint

    </DisplayString>

    <DisplayString

      Condition="restype==1070">

      {resval.rint} rint

    </DisplayString>

 

    <!--DXF/XData Int32-->

    <DisplayString

      Condition="(restype&gt;=90) &amp;&amp; (restype&lt;=99)">

      {resval.rlong} rlong

    </DisplayString>

    <DisplayString

      Condition="(restype&gt;=420) &amp;&amp; (restype&lt;=429)">

      {resval.rlong} rlong

    </DisplayString>

    <DisplayString

      Condition="(restype&gt;=440) &amp;&amp; (restype&lt;=459)">

      {resval.rlong} rlong

    </DisplayString>

    <DisplayString

      Condition="restype==1071">

      {resval.rlong} rlong

    </DisplayString>

 

    <!--DXF/XData ObjectId-->

    <DisplayString

      Condition="(restype&gt;=330) &amp;&amp; (restype&lt;=339)">

      soft pointer id

    </DisplayString>

    <DisplayString

      Condition="(restype&gt;=340) &amp;&amp; (restype&lt;=349)">

      hard pointer id

    </DisplayString>

    <DisplayString

      Condition="(restype&gt;=350) &amp;&amp; (restype&lt;=359)">

      soft owner id

    </DisplayString>

    <DisplayString

      Condition="(restype&gt;=360) &amp;&amp; (restype&lt;=369)">

      hard owner id

    </DisplayString>

    <DisplayString

      Condition="(restype&gt;=390) &amp;&amp; (restype&lt;=399)">

      hard pointer id

    </DisplayString>

 

    <!--DXF/XData 8bit int-->

    <DisplayString

      Condition="(restype&gt;=280) &amp;&amp; (restype&lt;=289)">

      {resval.rint} 8-bit rint

    </DisplayString>

    <DisplayString

      Condition="(restype&gt;=290) &amp;&amp; (restype&lt;=299)">

      {resval.rint} bool rint

    </DisplayString>

 

    <!--DXF/XData Binary Chunk -->

    <DisplayString

  
0;  
Condition="(restype&gt;=310) &amp;&amp; (restype&lt;=319)">

      binary size={resval.rbinary.clen}

    </DisplayString>

    <DisplayString

      Condition="restype==1004">

      binary size={resval.rbinary.clen}

    </DisplayString>

 

    <!--DXF/XData Int64 -->

    <DisplayString

      Condition="(restype&gt;=160) &amp;&amp; (restype&lt;=169)">

      {resval.mnInt64} int64

    </DisplayString>

 

    <Expand>

      <Item Name="rbnext">rbnext</Item>

      <Item Name="restype">restype</Item>

    </Expand>

  </Type>

 

</AutoVisualizer>

To see the difference it makes to variable viewing, here's how the debugger displays various kinds of AcStrings by default without the .natvis file:

+ acs1 {mnFlags=4 '\x4' mptr={mnPad2=0x0000004eaddde519 "" mpwszData=0x0000004ebabe1860 L"This is an AcString" ...} ...} AcString

+ acs2 {mnFlags=3 '\x3' mptr={mnPad2=0x0000004eaddde549 "" mpwszData=0x0000004ebabe1920 L"桴獩椠⁳湡愠獮⁩捁瑓楲杮ﷲ﷽꯽ꮫꮫꮫꮫꮫꮫꮫﺫﻮﻮﻮﻮﻮ" ...} ...} AcString

+ acs3 {mnFlags=1 '\x1' mptr={mnPad2=0x0000004eaddde579 "a" mpwszData=0xcccccccccccccccc ...} ...} AcString

+ acs4 {mnFlags=2 '\x2' mptr={mnPad2=0x0000004eaddde5a9 "" mpwszData=0xcccccccccccccccc ...} ...} AcString

+ acs5 {mnFlags=0 '\0' mptr={mnPad2=0x0000004eaddde5d9 "" mpwszData=0x0000000000000000 mpszData=0x0000000000000000 ...} ...} AcString

+ acs6 {mnFlags=5 '\x5' mptr={mnPad2=0x0000004eaddde609 "" mpwszData=0x0000004eb4cf2780 L"■듏N" mpszData=0x0000004eb4cf2780 " %Ï´N" ...} ...} AcString

Here's how these are displayed using acad.natvis:

+ acs1 L"This is an AcString" AcString

+ acs2 "this is an ansi AcString" AcString

+ acs3 "a" AcString

+ acs4 L"u" AcString

+ acs5 "" AcString

+ acs6 L"this is both unicode and ansi" AcString

A big improvement, I'm sure you'll agree. Many thanks to Davis and Augusto for making this happen!

3 responses to “VS2012 debugger type visualizers for ObjectARX”

  1. Great work! It will help a lot...

  2. Under Windows7/8 it's %UserProfile%\Documents\... and not "My Documents\..."

    1. Thanks for that - I had only used the other location (and this piece of text was copied verbatim from the Microsoft blog post).

      Kean

Leave a Reply to tbrammer Cancel reply

Your email address will not be published. Required fields are marked *