Debug Your PixTools Based Applications With Ease

Contributed by - Dmitry Kolomiets

 

Abstract

PixTools for .NET has a lot of data structures which can be visualized. In this how to article we'll demonstrate how to create two simple visualizers for both the PixImage object and the PixNode tree hierarchy.

 

Download Sample Code Demonstrating the Techniques Explained in this Article

Solution folder should be placed in the "Samples" directory of PixTools for.NET installation path. Example:

"C:\Program Files\EMC Captiva\PixTools for .NET\Samples\Visualizers"

 

The sample code is in the attached .zip file.

 

Visualizers

Visual Studio IDE provides capabilities to visualize debugging information. For example, visualizers are used to show text information from class properties and members:

 

 

The Small magnifier icon is used to show the visualizer:

 

Architecture

The Debugger visualizers architecture is shown in Figure 1.

 

 

Figure 1

 

A debuggee process is the process under debugging. It contains some objects such as strings, integers, .NET framework structs and classes, user custom types and so forth. At the other side there is a debugger process which shows some the user interface to visualize the debuggee's objects. Object of interest should be passed between the processes to visualize. The Debugger process could change the object and pass it back to the debugge process, so debug visualizers can be used not only for viewing but also for editing purposes.

 

To create a simple debug visualizer you should create a class library with a subclass of the Microsoft.VisualStudio.DebuggerVisualizers.DialogDebuggerVisualizer class and a override Show method. That's all! The Show method has two parameters: IDialogVisualizerService interface which has only one method - ShowDialog which is used by the debug visualizer to show some user interface. There are several overloads of the following methods:

 

  • ShowDialog(CommonDialog)

  • ShowDialog(Control)

  • ShowDialog(Form)

 

The Second parameter of the Show method is IVisualizersObjectProvider interface which is used to retrieve object of interest and communicate with the debuggee process to pass a changed object back, if needed. To register the visualizer for a specified type, the attribute DebuggerVisualizerAttribute is used at the assembly level. All visualizer assemblies should be placed in the following folder: <Visual Studio directory>\Common7\Packages\Debugger\Visualizers

 

Simple PixImage Debugger Visualizer

The Following source code shows how to create a visualizer for the PixImage objects. Implementation of the Show method is obvious: it creates a new form, checks whether the image initialized, and then creates the PixView control to show the image. The IDialogVisualizerService.ShowDialog method is used to show the visualizer form.

 

    

using Microsoft.VisualStudio.DebuggerVisualizers;

using System.Windows.Forms;

using System.Drawing;

using PixTools.WinControls;

 

[assembly: System.Diagnostics.DebuggerVisualizer(

    typeof(PixTools.DebugVisualizers.PixImageDebugVisualizer),

    typeof(VisualizerObjectSource),

    Target = typeof(PixTools.PixImage),

    Description = "PixImage Visualizer")

]

 

namespace PixTools.DebugVisualizers {

 

    public class PixImageDebugVisualizer: DialogDebuggerVisualizer

    {

        override protected void Show(IDialogVisualizerService windowService, IVisualizerObjectProvider objectProvider)

        {

            PixImage image = (PixImage)objectProvider.GetObject();

            Form form = new Form();

            form.ClientSize = new Size(300, 300);

            form.FormBorderStyle = FormBorderStyle.SizableToolWindow;

           

            if (image.Initialized)

            {

                form.Text = string.Format("Width: {0}, Height: {1} - PixImage Visualizer", image.Width, image.Height);

                PixView view = new PixView();

                view.ScaleMode = PixScaleMode.FitPage;

                view.Parent = form;

                view.Dock = DockStyle.Fill;

                view.Image = image;

            }

           

            windowService.ShowDialog(form);

        }

    }

}

 

After installation of the visualizer assembly to the Visual Studio folder, the magnifier icon will appear for the PixImage objects during debugging (Figure 2).

Figure 2

 

The visualizer window is shown in Figure 3.

Figure 3

Simple PixNode hierarchy visualizer

The PixNode type was designed to represent a tree of nodes, so during debugging it may be interesting to see the whole tree and the location of a particular node within the tree. The Visualizer's logic is very simple. Fisrt locate the root node of the tree. Each PixNode object has a Parent property which can be used to retrieve the parent node. For the root node Parent the value is null. The Following method is used to locate the root of the tree:

 

    

private static PixNode GetParent(PixNode child)

{

    while (child.Parent != null)

        child = child.Parent;

    return child;

}

 

When the root node is found a recursive method is instantiated to build the whole tree structure. The Visualizer uses the System.Windows.Forms.TreeView control to represent the tree, and the BuildTree method returns the root System.Windows.Forms.TreeNode object:

 

    

private static TreeNode BuildTree(PixNode parent, PixNode sourceNode)

{

    TreeNode parentNode = CreateTreeNode(parent, sourceNode);

    foreach (PixNode node in parent.Children)

    {

        if (node.Type == PixNodeType.ContainerNode)

            parentNode.Nodes.Add(BuildTree(node, sourceNode));

        else

        {

            TreeNode treeNode = CreateTreeNode(node, sourceNode);

            parentNode.Nodes.Add(treeNode);

        }

    }

    return parentNode;

}

 

The Helper method CreateTreeNode creates the TreeNode objects, and highlites the node in red if it represents the node for which debugger visualizer was called:

 

    

private static TreeNode CreateTreeNode(PixNode node, PixNode sourceNode)

{

    TreeNode treeNode = new TreeNode((node.Name.Equals(String.Empty) ? "node" : node.Name));

    if (node == sourceNode)

        treeNode.ForeColor = Color.DarkRed;

    return treeNode;

}

 

All parts of the mosaic are collected together in the following code:

 

    

using System;

using Microsoft.VisualStudio.DebuggerVisualizers;

using System.Windows.Forms;

using System.Drawing;

using PixTools.PixDoc;

 

[assembly: System.Diagnostics.DebuggerVisualizer(

typeof(PixTools.DebugVisualizers.PixNodeDebugVisualizer),

typeof(VisualizerObjectSource),

Target = typeof(PixTools.PixDoc.PixNode),

Description = "PixNode Visualizer")]

namespace PixTools.DebugVisualizers

{

    public class PixNodeDebugVisualizer : DialogDebuggerVisualizer

    {

        override protected void Show(IDialogVisualizerService windowService,

                                                                 IVisualizerObjectProvider objectProvider)

        {

            PixNode node = (PixNode)objectProvider.GetObject();

 

            Form form = new Form();

            form.Text = "PixNode visualizer";

            form.ClientSize = new Size(300, 300);

            form.FormBorderStyle = FormBorderStyle.SizableToolWindow;

 

            TreeView tree = new TreeView();

            tree.Parent = form;

            tree.Dock = DockStyle.Fill;

 

 

            PixNode parent = GetParent(node);

            tree.Nodes.Add(BuildTree(parent, node));

 

            windowService.ShowDialog(form);

        }

 

        private static TreeNode BuildTree(PixNode parent, PixNode sourceNode)

        {

            TreeNode parentNode = CreateTreeNode(parent, sourceNode);

 

            foreach (PixNode node in parent.Children)

            {

 

                if (node.Type == PixNodeType.ContainerNode)

                    parentNode.Nodes.Add(BuildTree(node, sourceNode));

                else

                {

                    TreeNode treeNode = CreateTreeNode(node, sourceNode);

                    parentNode.Nodes.Add(treeNode);

                }

            }

 

            return parentNode;

        }

 

        private static PixNode GetParent(PixNode child)

        {

            while (child.Parent != null)

                child = child.Parent;

 

            return child;

        }

 

        private static TreeNode CreateTreeNode(PixNode node, PixNode sourceNode)

        {

            TreeNode treeNode = new TreeNode((node.Name.Equals(String.Empty) ? "node" : node.Name));

            if (node == sourceNode)

                treeNode.ForeColor = Color.DarkRed;

            return treeNode;

        }

    }

}

 

Figure 4 illustrates the PixNode visualizer in action.

Figure 4

 

About the Author

Dmitry Kolomiets is the software engineer for the PixTools technology group. He graduated St. Petersburg State Polytechnical University. Mr. Kolomiets has a lot of experience with .NET internals and participated in PixTools for .NET project from the beginnin