Visual Studio DebuggerVisualizers: what I wish I’d known

A custom Visualizer for the Visual Studio debugger can provide a huge boost to productivity.  I’m not sure I could manage without them.  However, they can be tricky to write and even trickier to get working.

There are quite a few articles and posts about the Visual Studio DebuggerVisualizer mechanism.  Unfortunately, the Microsoft documentation isn’t exactly thorough.  Here are a few things I wish I’d known when I was writing them:

What can you visualize?

Microsoft says “You can write a custom visualizer for an object of any managed class except for Object or Array. ” http://msdn.microsoft.com/en-us/library/e2zc529c.aspx .

It’s worth noting that if your variables are interfaces, then the object you’re visualizing corresponds to the implementer, not the interface.  So if you have:

interface Ia
{
    ...
}
class B: Ia
{
    ...
}
B b = new B();
Ia a = (Ia)b;

… Then it’s clear that if you point to b, you’re trying to visualize an object of type B and your visualizer will need to be attributed accordingly.  What’s not quite so obvious (at least, not to me) is that the same visualizer will work if you point to a, and my assumption is that there’s no point in having a visualizer for type Ia; it will never be used because the debugger will consider the underlying implementer, not the interface itself.

Serialization

Despite many articles suggesting this to be the case, the class you want to debug does not need to be marked as ISerializable, although it makes things slightly easier if it is.  You do need to “serialize” some or all of the object (to the extent that you want to visualize it), because you need to transfer it from the debuggee (the process you’re debugging) to the debugger (Visual Studio’s process).

VisualizerObjectSource

This object runs in the Debuggee process, and is responsible for serializing the object you want to visualize, to the debugger.  IF your object is marked as ISerializable, then you can use the standard class, VisualizerObjectSource.  If not, you must:

  • Derive your own class from VisualizerObjectSource (say, DebuggeeSideVizSource, and override its GetData method
  • Mark your DebuggerVisualizer object as using your class as the source:
[assembly: System.Diagnostics.DebuggerVisualizer(
typeof(Visualizer.DebuggerSideViz),
typeof(Visualizer.DebuggeeSideVizSource),
Target = typeof(Bitmap),
Description = "Bitmap Visualizer")]

(Where Visualizer.DebuggerSideViz is your visualizer class, and Visualizer.DebuggeeSideVizSource is the class derived from VisualizerObjectSource)

  • If you have a “TestShowVisualizer” method for testing your visualizer, make sure that you use the variant of the VisualizerDevelopmentHost constructor that allows you to specify the source:
public static void TestShowVisualizer(object objectToVisualize)
{
    VisualizerDevelopmentHost visualizerHost = new
        VisualizerDevelopmentHost(objectToVisualize,
        typeof(DebuggerSideViz),
        typeof(DebuggeeSideVizSource));
        visualizerHost.ShowVisualizer();
}

(If you forget this, you’ll see a “Type is not marked as serializable” exception when you try to run your test application).

Note that it may be convenient to transform your object, or whatever aspects of it you want to visualize, before serializing it.  For example, if your visualizer is for a type of image that is not serializable, your VisualizerObjectSource-derived class’s GetObject method could transform it into a Bitmap and then serialize it (easy because a Bitmap is serializable).  For example:

public override void GetData(object inObject, Stream outStream)
{
    if (inObject != null)
    {
        if (inObject is MyImageClass)
        {
            Bitmap bmp = ((MyImageClass)inObject).AsBitmap();
            BinaryFormatter bf = new BinaryFormatter();
            bf.Serialize(outStream, bmp);
        }
    }
}

Timeouts

If your object takes too long to serialize across to the debugger, Visual Studio decides something is wrong, and times out.  The timeout period is surprisingly short.  What you see is an exception with the message “Function evaluation timed out”.  You can continue without problem, but you haven’t seen your visualization.

There are registry settings you can edit to change the timeout period.  They are all beneath HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\10.0\Debugger.

The relevant keys are NormalEvalTimeout, QuickwatchTimeout and DataTipTimeout and they are in milliseconds.

Of these, DataTipTimeout seems to be the crucial one.  Note that these values must only be changed when Visual Studio is not running (VS probably reads it on startup and then writes it on shutdown.)

Deployment

Probably best to put the visualizer assembly into the “My documents\Visual Studio YYYY\Visualizers” folder (the alternative is within the Visual Studio installation directory).

You can add post-build events to copy it as follows:

xcopy /fy “$(TargetPath)” “$(USERPROFILE)\Documents\Visual Studio 2010\Visualizers”

Make sure you also copy all support dlls required to interpret the data.  It’s a good idea to use additional commands in the post-build event to achieve this:

xcopy /fy “$(TargetDir)XXX.dll” “$(USERPROFILE)\Documents\Visual Studio 2010\Visualizers”

Testing

It’s a good idea to have a console-based test program, as described in the Microsoft documentation.  But it’s even better if you copy the test exe to the Visualizers folder and run it there, because then it’s also testing the correct deployment of your visualizer assembly and its support assemblies.

Testing with Visual Studio

Remember that you need to restart VS to have it load your Visualizer assembly.

 

This entry was posted in Uncategorized. Bookmark the permalink.