Introduction
This explains how to change how the visual studio native debugger displays data for very complicated types. It explains how to change this:

to this:

In other words, it greatly simplifies displaying native code in the debugger window.
This is not a tutorial on how to modify or customize autoexp.dat in general. This is a very specific tutorial on how to custom the debugger variable display for types that are very complicated and involve deep aggregation or inheritance.
Problem
Last year, I was trying to debug a particular problem and found it difficult to view a particular data type in the visual studio debugger. This particular data type was way over engineered and complicated just for the sake of being complicated. It was a class that simply held a string and did path operations on the string. But it held the string in a complicated morass of class hierarchies that made displaying the actual string very difficult in the visual studio native debugger. So I embarked on a task to display the data I wanted in the debugger windows. This is possible by modifying the file:
“C:\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\Packages\Debugger\autoexp.dat”
I have created a demo class hierarchy that is needlessly complex and demonstrates the problem that you have to drill down deep in the VS debugger to see the data. I also included code to run it:
- // NativeVisualizerTest.cpp : Defines the entry point for the console application.
- //
- #include “stdafx.h”
- #include <string>
- #include <memory> // for autoptr
- // The inner string, taken from std::string
- typedef std::basic_string<TCHAR> StupidString;
- // Another string that makes debugging that much harder
- class SpecialString : public StupidString
- {
- public:
- SpecialString() {}
- SpecialString(TCHAR* s) : StupidString(s) {}
- ~SpecialString() {}
- };
- // Another class that wraps the SpecialString
- class PathPrivateInternal
- {
- public:
- PathPrivateInternal() {}
- PathPrivateInternal(TCHAR* s)
- : mString(s) {}
- ~PathPrivateInternal(){}
- private:
- SpecialString mString;
- };
- // The final class that holds a PathPrivateInternal
- // Visualizing this crazy class in the debugger will NOT be fun!
- class Path
- {
- public:
- Path()
- : mPtr(new PathPrivateInternal())
- { }
- Path(TCHAR* s)
- : mPtr(new PathPrivateInternal(s))
- { }
- ~Path() { }
- private:
- std::auto_ptr<PathPrivateInternal> mPtr;
- };
- int _tmain(int argc, _TCHAR* argv[])
- {
- StupidString str(_T(“I am a foo bar”));
- SpecialString strB(_T(“I am a foo bar”));
- PathPrivateInternal ppi(_T(“I am a foo bar”));
- Path p(_T(“I am a foo bar”));
- return 0;
- }
So in this example, I have three concrete classes, a smart pointer (i.e. std::autoptr), and a typedef that have to be deciphered in order to display the string I really want to see. This is really a yucky mess: all for a class to simply hold a string and perform path like operations on it.
Demonstration
If I put a breakpoint in line 54, and add p to the watch window, I will see this in the debugger:

Notice how many sub tree’s I had to open in the debugger in order to see the string? !! Yuck !!
Unraveling the Gordian knot
The trick to displaying this in the debugger is to write a visualizer for each different class in this hierarchy. First you start from the inner class, and work towards the outer class. In this example the inner class is std::basic_string<TCHAR> and the outer string is Path.
The following list shows the order we need to write visualizers:
|
Type |
| StupidString |
| SpecialString |
| PathPrivateInternal |
| std::autoptr |
| Path |
StupidString
In the case of StupidString we are in luck. autoexp.dat already contains a visualizer for std::basic_string that displays the data nicely.

But the other three types do not display helpful information in the preview window.
SpecialString
Most of the work to visualize all the data types takes place here in SpecialString. The class SpecialString isn’t a mere typedef, so there is a little more work to get it to display in the debugger. The trick is to treat it like a std::basic_string. So find the section in autoexp.dat that displays std::basic_string and plagiarize that code like crazy. Except don’t copy the children section.
Here is the ANSI string version of std::basic_string
- ;——————————————————————————
- ; std::string/basic_string
- ;——————————————————————————
- std::basic_string<char,*>{
- preview ( #if(($e._Myres) < ($e._BUF_SIZE)) ( [$e._Bx._Buf,s]) #else ( [$e._Bx._Ptr,s]))
- stringview ( #if(($e._Myres) < ($e._BUF_SIZE)) ( [$e._Bx._Buf,sb]) #else ( [$e._Bx._Ptr,sb]))
- children
- (
- #if(($e._Myres) < ($e._BUF_SIZE))
- (
- #([actual members]: [$e,!] , #array( expr: $e._Bx._Buf[$i], size: $e._Mysize))
- )
- #else
- (
- #([actual members]: [$e,!], #array( expr: $e._Bx._Ptr[$i], size: $e._Mysize))
- )
- )
- }
In this code, all that is needed is the “preview” code in line 5. Now there is a nice gotcha here. In order to properly display the type for SpecialString this program has to be compiled for non Unicode. That is because the visualizer for std::basic_string plagiarized from above is for <char> types. If I want to compile for Unicode, I need to plagiarize the following code (some stuff omitted for clarities sake):
- std::basic_string<unsigned short,*>|std::basic_string<wchar_t,*>{
- preview
- (
- #if(($e._Myres) < ($e._BUF_SIZE)) ( [$e._Bx._Buf,su] )
- #else ( [$e._Bx._Ptr,su] )
- )
- stringview
- (
- #if(($e._Myres) < ($e._BUF_SIZE)) ( [$e._Bx._Buf,sub] )
- #else ( [$e._Bx._Ptr,sub] )
- )
- …
- }
The stringview part allows you to display your data Text Visualizer dialog. Copy that to the SpecialString visualizer too:
- SpecialString{
- preview ( #if(($e._Myres) < ($e._BUF_SIZE)) ( [$e._Bx._Buf,s]) #else ( [$e._Bx._Ptr,s]))
- stringview ( #if(($e._Myres) < ($e._BUF_SIZE)) ( [$e._Bx._Buf,sb]) #else ( [$e._Bx._Ptr,sb]))
- }

Now the debugger properly displays the string in the watch windows…

Notice now that the last two classes PathPrivateInternal and Path both now show some useful information in their preview windows. This is good, but not the final solution.
PathPrivateInternal
Now to visualize class PathPrivateInternal, will take just a little work.
- PathPrivateInternal{
- preview (
- [$e.mString]
- )
- }
Notice I only had to specify the member variable mString. There was no need to specify a type to render the data as, since the debugger already knows that mString is of type SpecialString. And the debugger already knows how render that. Here are the results:

Path
The last part is to specify the visualizer for class Path.
- Path{
- preview (
- [$e.mPtr]
- )
- }
Which displays this:

However notice that it rendered the text as auto_ptr “I am a foo bar”. This is because of the visualizer for auto_ptr, which is also included in autoexp.dat:
- ;——————————————————————————
- ; std::auto_ptr
- ;——————————————————————————
- std::auto_ptr<*>{
- preview
- (
- #(
- “auto_ptr “,
- (*(($T1 *)$e._Myptr))
- )
- )
- children
- (
- #(
- ptr: (*(($T1 *)$e._Myptr))
- )
- )
- }
Notice the hard coded string in the preview section. Also notice that the template type in the auto_ptr declarations is a star (*) meaning use this visualizer for all type’s that auto_ptr is using. There are a few things that need to be done to fix this. First, simply specialize the visualizer for when auto_ptr is holding a type of PathPrivateInternal.
- std::auto_ptr<PathPrivateInternal>{
- preview
- (
- [$e._Myptr]
- )
- }
Now, in the autoexp.dat file there are three sections, listed in order:
- [AutoExpand]
- [Visualizer]
- [hresult]
If the custom visualizer for std::auto_ptr<PathPrivateInternal> is placed after std::auto_ptr<*> then this custom visualizer will get ignored. This is because the debugger uses the first visualizer that it finds in the autoexp.dat file that satisfies the type criteria. And the star (*) template type unfortunately accepts all types including the std::auto_ptr<PathPrivateInternal>. Therefore put all custom visualizers at the beginning of the Visualizer section, NOT the end. After doing that, you will get the results shown below:

Here is another view with the Path type expanded:

Summary
Here is the final listing of all the visualizers:
- ;——————————————————————————
- ; My Custom Types
- ;——————————————————————————
- SpecialString{
- preview ( #if(($e._Myres) < ($e._BUF_SIZE)) ( [$e._Bx._Buf,s]) #else ( [$e._Bx._Ptr,s]))
- stringview ( #if(($e._Myres) < ($e._BUF_SIZE)) ( [$e._Bx._Buf,sb]) #else ( [$e._Bx._Ptr,sb]))
- }
- PathPrivateInternal{
- preview (
- [$e.mString]
- )
- }
- std::auto_ptr<PathPrivateInternal>{
- preview
- (
- [$e._Myptr]
- )
- }
- Path{
- preview (
- [$e.mPtr]
- )
- }
And remember these main points:
- Start with inner nested types and work your way outwards
- Copy from STL types when needed.
- Put all custom visualizers at the beginning of the [Visualizer] section in autoexp.dat
- Use stringview to display text in the Text Visualizer if needed.
- Use specialized template types to display special cases if needed.
great post! great article!
Link | March 1st, 2010 at 11:25 pm
Nice. This makes C# and Java ToString() look like heaven
But at least this is do-able in C++ too, if somewhat painful.
Link | April 3rd, 2010 at 9:51 pm
I need to debug the graphics program, and I want to get the position info more easier.
Thanks to the article, saving me a lot of debugging time!
Link | October 25th, 2010 at 4:32 am
And there is another yuck when text parser.
e.g. “vector” is not valid because there is a blank between “int,” and “*”!!!
Link | October 25th, 2010 at 6:05 am
Nice article. A good read. Will definitely help me debug better.
Link | November 10th, 2010 at 11:00 am
Debugger visualizer of GDI objects for unmanaged C++ | trouble86.com wrote:
[...] from the MSDN documentation and examples, I found this: http://www.idigitalhouse.com/Blog/?p=83 … however, it “only” covers textual data. Virtually all other information was [...]
Link | November 18th, 2011 at 8:49 am
What do I do if there were no visualizer for StupidString?
Suppose I want to display the first byte as binary data: 00101110.
Can that be done with this trick?
Link | June 9th, 2012 at 8:53 am