As promised, an updated Visualizers How-To! My last how-to entry on Visualizers was targeted at the PDC release of Whidbey, as you may (or may not) remember. This time I don't feel the need to cringe at times as I describe the process. :0)

First, a high level overview of Visualizers. Visualizers allow for advanced, customized viewing of data while debugging. Today's data windows have their limitations; text-only, hierarchical, spatially constrained - not the best for viewing an image, for example. Visualizers allow you to create completely custom views using WinForms to best show the data within any managed object. Yes, unfortunately this feature will only available for the managed world.

A Visualizer is associated with a particular type. Whenever that type is seen by the debugger, a little magnifying glass will be shown next to its entry on datatips, the watch window, quickwatch, etc. Multiple Visualizers can be associated with one type, and a Visualizer can be associated with multiple types. A class derived from a class with Visualizers associated with it will inherit those associations, but Visualizers cannot be associated with interfaces (if you think this is an important scenario, let me know why).

Alright, let's get started.

First, create a new C# Class Library Project, perhaps named FirstVisualizer. To start off, you'll want to add a reference to meehost.dll; browse to this location in the Add References dialog: <VS Install Dir>\Common7\IDE\meehost.dll. Now we have access to all the interfaces and base classes we need to be able to communicate with Visual Studio.

Next open the auto-generated Class1.cs file. In the Solution Explorer tool window, right-click on it and rename it to "DebuggerSide.cs" - note that the IDE intelligently renames the class and constructor name in source as well. DebuggerSide is an appropriate name because this is the class which is loaded into Visual Studio to actually display the data. To make it easier to talk to the Visualizer interfaces, add 'using System.Diagnostics' to the top of the file. Next, type ': IDebugVisualizer' after 'class DebuggerSide' to implement the IDebugVisualizer interface defined in meehost.dll.

Now click on IDebugVisualizer - a small glyph will appear under the 'I' - a Smart Tag. Hover over it, click on the resulting larger icon, then select one of the options on the menu. The IDE will auto-generate the appropriate stubs to implement the very simple IDebugVisualizer interface.

Hovering over IDebugVisualizer in Visual Studio will give you a Smart Tag. Use this to help you implement the interface.

The Show method you should now see in front of you is what is called when you click to see the visualizer while debugging. Let's add something useful to it. First, add System.Windows.Forms to your references - we need to show something graphical here. Optionally delete System.Data and System.XML, since we won't be using those (this can also be done with the using statements at the top - we only need System, System.Diagnostics, and System.Windows.Forms for this example). Now we can change 'throw new System.NotImplementedException();' to 'MessageBox.Show(objectProvider.GetObject().ToString());', which of course lacks necessary error-checking for possible nulls. I'm willing to take that risk for brevity. :0)

This simple visualizer just pops up a message box with the string representation of whatever object it gets.

Now that we've created the visualizer. We need to tell Visual Studio that it is there, what to do with it. We've added a number of attributes for VS 2005 that will allow you to customize your debugging experience - DebuggerVisualizer is just one of them. It is defined in the BCL, and has a lot of constructors.

Target = typeof(System.String),
Description = "My First Visualizer")]
  • parameter 1: the debuggee-side class, responsible for serializing the data inside the object in question to a stream which is received by the debugger-side class. VisualizerObjectSource is a class meehost.dll provides which serializes the entire object object across. This is all hidden because in DebuggerSide.Show we just call GetObject, which assumes that the entire object has been serialized into the stream.
  • parameter 2: the debugger-side class, responsible for catching the data thrown across by the debuggee-side class and showing it in a meaningful way.
  • parameter 3: the UI type of the visualizer - this is currently unsupported, though the enum does mention drop-down and tool window as UI types. Would these be useful to you? Let me know.
  • named parameter: Target: Attributes are generally put directly onto their targets. This named parameter allows the attribute to be used at the assembly level.
  • named parameter: Description: this is the text shown in the drop-down in the watch window. It's a bit of a problem for internationalization, actually. We wanted to avoid loading any visualizer code before the user actually invokes it; that has resulted in this problem. We're looking at what we can do to solve this problem, but it's really a more general problem with text in attributes.
Now, build the solution. It builds, right? :0) Now we need to copy the DLL we just created to the place VS expects to see visualizers. On my machine, my DLL was built to My Documents\Visual Studio\Projects\FirstVisualizer\FirstVisualizer\bin\Debug. Copy the DLL to <VS Install Dir>\Common7\Packages\Debugger\attribcache. In that same directory you will see Microsoft.VisualStudio.Debugger.DataSetVisualizer.dll, which is the dataset visualizer we provide. In the future this folder won't be named this, and we will be providing a per-user Visualizers folder in the Visual Studio hierarchy under My Documents.

Now for the fun part. Right-click on your current solution in VS, click Add Project, and add a Console or Windows Application project. Make sure it is the new startup project, then add some code which uses a string, set a breakpoint, and press F5.

DataTips show while hovering over the string variable blah - 'My First Visualizer' is included in the list of Visualizers for it.

First, some bad news: unfortunately, invoking custom visualizers on strings from DataTips is broken in this build. Sorry. The good news is that your custom visualizer will work from any other data window.

The visualizer has been  invoked, and the MessageBox shows the contents of the string.

When you invoke the visualizer, the MessageBox pops up with the contents of the string in it! Cool, eh? As with before, this visualizer is extremely trivial, and definitely pointless. It does, however, give you the basic method - you can easily build on top of these steps to create much more advanced, useful Visualizers.

I'll be writing another post soon addressing some more advanced topics with Visualizers, like performance. Using the above methods, you are sending the entire object around all the time. I'll also talk a little more in depth about the interfaces, like the IVisualizerObjectProvider passed in on the Show method.

Feeling a little lazy?