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<*>">
<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>=1) && (restype<=9)">
{resval.rstring}
</DisplayString>
<DisplayString
Condition="(restype>=100) && (restype<=103)">
{resval.rstring}
</DisplayString>
<DisplayString Condition="restype==105">
{resval.rstring}
</DisplayString>
<DisplayString
Condition="(restype>=300) && (restype<=309)">
{resval.rstring}
</DisplayString>
<DisplayString Condition="restype==410">
{resval.rstring}
</DisplayString>
<DisplayString
Condition="(restype>=430) && (restype<=439)">
{resval.rstring}
</DisplayString>
<DisplayString
Condition="(restype>=470) && (restype<=479)">
{resval.rstring}
</DisplayString>
<DisplayString
Condition="(restype>=999) && (restype<=1003)">
{resval.rstring}
</DisplayString>
<!--DXF/XData Double-->
<DisplayString
Condition="(restype>=38) && (restype<=59)">
{resval.rreal} rreal
</DisplayString>
<DisplayString
Condition="(restype>=140) && (restype<=149)">
{resval.rreal} rreal
</DisplayString>
<DisplayString
Condition="(restype>=460) && (restype<=469)">
{resval.rreal} rreal
</DisplayString>
<DisplayString
Condition="(restype>=1040) && (restype<=1042)">
{resval.rreal} rreal
</DisplayString>
<!--DXF/XData Point-->
<DisplayString
Condition="(restype>=10) && (restype<=17)">
{resval.rpoint[0]},{resval.rpoint[1]},{resval.rpoint[2]}
</DisplayString>
<DisplayString
Condition="(restype>=110) && (restype<=112)">
{resval.rpoint[0]},{resval.rpoint[1]},{resval.rpoint[2]}
</DisplayString>
<DisplayString
Condition="(restype>=210) && (restype<=219)">
{resval.rpoint[0]},{resval.rpoint[1]},{resval.rpoint[2]}
</DisplayString>
<DisplayString
Condition="(restype>=1010) && (restype<=1013)">
{resval.rpoint[0]},{resval.rpoint[1]},{resval.rpoint[2]}
</DisplayString>
<!--DXF/XData Int16-->
<DisplayString
Condition="(restype>=60) && (restype<=79)">
{resval.rint} rint
</DisplayString>
<DisplayString
Condition="(restype>=270) && (restype<=279)">
{resval.rint} rint
</DisplayString>
<DisplayString
Condition="(restype>=370) && (restype<=389)">
{resval.rint} rint
</DisplayString>
<DisplayString
Condition="(restype>=400) && (restype<=409)">
{resval.rint} rint
</DisplayString>
<DisplayString
Condition="restype==1070">
{resval.rint} rint
</DisplayString>
<!--DXF/XData Int32-->
<DisplayString
Condition="(restype>=90) && (restype<=99)">
{resval.rlong} rlong
</DisplayString>
<DisplayString
Condition="(restype>=420) && (restype<=429)">
{resval.rlong} rlong
</DisplayString>
<DisplayString
Condition="(restype>=440) && (restype<=459)">
{resval.rlong} rlong
</DisplayString>
<DisplayString
Condition="restype==1071">
{resval.rlong} rlong
</DisplayString>
<!--DXF/XData ObjectId-->
<DisplayString
Condition="(restype>=330) && (restype<=339)">
soft pointer id
</DisplayString>
<DisplayString
Condition="(restype>=340) && (restype<=349)">
hard pointer id
</DisplayString>
<DisplayString
Condition="(restype>=350) && (restype<=359)">
soft owner id
</DisplayString>
<DisplayString
Condition="(restype>=360) && (restype<=369)">
hard owner id
</DisplayString>
<DisplayString
Condition="(restype>=390) && (restype<=399)">
hard pointer id
</DisplayString>
<!--DXF/XData 8bit int-->
<DisplayString
Condition="(restype>=280) && (restype<=289)">
{resval.rint} 8-bit rint
</DisplayString>
<DisplayString
Condition="(restype>=290) && (restype<=299)">
{resval.rint} bool rint
</DisplayString>
<!--DXF/XData Binary Chunk -->
<DisplayString
Condition="(restype>=310) && (restype<=319)">
binary size={resval.rbinary.clen}
</DisplayString>
<DisplayString
Condition="restype==1004">
binary size={resval.rbinary.clen}
</DisplayString>
<!--DXF/XData Int64 -->
<DisplayString
Condition="(restype>=160) && (restype<=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!