Click here to Skip to main content
Click here to Skip to main content

Graphics Debugger Visualizer

, 8 Mar 2008
Rate this:
Please Sign up or sign in to vote.
A debugger visualizer for managed Graphics objects
AVG_certification

Introduction

Microsoft made it fairly easy to implement a custom debugger visualizer for Visual Studio and there are numerous samples on the Web.
AFAIG - as far as I can Google - a working visualizer for Graphics objects has not been published yet. The type handled by the debugger visualizer must be streamable and Graphics is not serializable by default.

Credits

Background

The solution consists of using a serializable wrapper that extracts the Graphics contents to a bitmap, which is a serializable object.

[Serializable]
public sealed class SerializableGraphics : IDisposable
{
    private readonly Bitmap bitmap;

    public SerializableGraphics(Graphics graphics)
    {
        if (graphics == null)
        {
            throw new ArgumentNullException("graphics");
        }

        FieldInfo fi = graphics.GetType().GetField
            ("backingImage", BindingFlags.NonPublic | BindingFlags.Instance);
        if (fi != null)
        {
            Bitmap bm = (Bitmap)fi.GetValue(graphics);

            if (bm != null)
            {
                // graphics was derived from image : clone internal bitmap
                bitmap = (Bitmap)bm.Clone();
            }
            else
            {
                // graphics without backing image : bitblt to new bitmap
                Size sz = graphics.VisibleClipBounds.Size.ToSize();
                bitmap = new Bitmap(sz.Width, sz.Height, graphics);
                drawToBitmap(bitmap, graphics);
            }
        }
    }

    public Bitmap Bitmap
    {
        get { return bitmap; }
    }

    private static void drawToBitmap(Image bitmap, Graphics graphics)
    {
        using(Graphics g = Graphics.FromImage(bitmap))
        {
            IntPtr hdcDst = g.GetHdc();
            IntPtr hdcSrc = graphics.GetHdc();

            try
            {
                if (!SafeNativeMethods.BitBlt(
                    hdcDst, 0, 0, bitmap.Width, bitmap.Height,
                    hdcSrc, 0, 0, CopyPixelOperation.SourceCopy))
                {
                    throw new Win32Exception();
                }
            }
            finally
            {
                g.ReleaseHdc(hdcDst);
                graphics.ReleaseHdc(hdcSrc);
            }
        }
    }

    public void Dispose()
    {
        if (bitmap != null)
        {
            bitmap.Dispose();
        }
    }
}

A derived VisualizerObjectSource class streams the wrapper on the debuggee side.

internal class GraphicsVisualizerObjectSource : VisualizerObjectSource
{
    public override void GetData(object target, System.IO.Stream outgoingData)
    {
        Graphics data = (Graphics) target;
        base.GetData(new SerializableGraphics(data), outgoingData);
    }
}

On the debugger side, the data is unwrapped and displayed.

public class GraphicsVisualizer : DialogDebuggerVisualizer
{
    protected override void Show(IDialogVisualizerService windowService, 
            IVisualizerObjectProvider objectProvider)
    {
        using (SerializableGraphics wrapper = 
                (SerializableGraphics)objectProvider.GetObject())
        {
            using (BitmapVisualizerForm form = new BitmapVisualizerForm())
            {
                form.DebugBitmap = wrapper.Bitmap;
                windowService.ShowDialog(form);
            }
        }
    }
}

Finally the DebuggerVisualizerAttribute specifies our types at the assembly level.

[assembly: DebuggerVisualizer(typeof(GraphicsVisualizer), 
        typeof(GraphicsVisualizerObjectSource),
    Target = typeof(Graphics), Description = "Graphics debugger visualizer")]

Options

By default, the bitmap is shown unscaled and centered to the form. When zoomed, the larger bitmap width or height is clamped to the form's client area. By resizing the form, the current zoom factor is computed and displayed.
When debugging from a second Visual Studio instance, you can break code operation for 60 seconds. After that, Visual Studio may not be able to continue. Therefore I added a timer function that automatically closes the form after 30 seconds, --- it's time to hit F11/F5.
For convenience the chosen options and the form's last desktop bounds are persisted as a Visual Studio wide setting.

Other Goodies (See Source)

The System.Configuration.CommaDelimitedStringCollection and my GlobalsHelper class provide a flexible scheme to persist settings in the EnvDTE.Globals object. The Globals cache uses string name/value pairs and is available at Visual Studio, specific solution or project level.
The FormPlacement class was originally designed for restoring state of multiple forms using a single string key in Application Settings.

Improvements

My code provides just a simple readonly visualization, more info of the Graphics properties could be streamed as a string or custom class. The native hdc is serializable, and a new Graphics object can be displayed in a propertygrid using the static Graphics.FromHdc constructor. However I ran into problems of properly releasing the dc and dropped this in favor of the presented approach.
IDialogVisualizerService allows only to display modal dialogs. It would be nice to display graphics content in a modeless toolwindow, while stepping through code.

Using the Code

Drop the compiled DLL in ..\Microsoft Visual Studio 8\Common7\Packages\Debugger\Visualizers or the user-specific MyDocuments\Visual Studio 2005\Visualizers folder. When debugging and at a breakpoint, the little magnifying glass will appear in the datatip for Graphics objects.

Points of Interest

The code was tested on Visual Studio 2005/ Windows XP only. Note that, the ProgID of Visual Studio is hardcoded in the BitmapVisualizerForm.VS_DTE_VERSION constant.

History

  • 8th March, 2008: Article posted

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

OrlandoCurioso

Germany Germany
No Biography provided

Comments and Discussions

 
GeneralThanks PinmemberSaeed.39415-Aug-10 0:54 
GeneralVS2008 Compatibility notes Pinmemberdzzzen23-May-08 3:36 
GeneralRe: VS2008 Compatibility notes PinmemberOrlandoCurioso25-May-08 7:34 
AnswerRe: VS2008 Compatibility notes Pinmemberdzzzen25-May-08 9:45 
GeneralRe: VS2008 Compatibility notes PinmemberHardy Wang8-Feb-09 3:29 
AnswerRe: VS2008 Compatibility notes PinmemberJames R. Strawn4-Dec-09 9:38 
Generalvery nice Pinmembergeo_m12-Mar-08 3:53 
GeneralVS2008 Pinmembermarco_ragogna9-Mar-08 22:40 
AnswerRe: VS2008 PinmemberOrlandoCurioso10-Mar-08 1:42 
GeneralRe: VS2008 Pinmemberdzzzen23-May-08 3:40 
QuestionWhat a great idea :) Pinmember leppie 8-Mar-08 10:43 
AnswerRe: What a great idea :) PinmemberRoger Alsing8-Mar-08 13:04 
AnswerRe: What a great idea :) Pinmemberjomet9-Mar-08 23:38 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140709.1 | Last Updated 8 Mar 2008
Article Copyright 2008 by OrlandoCurioso
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid