Click here to Skip to main content
Email Password   helpLost your password?
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

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralVS2008 Compatibility notes
dzzzen
4:36 23 May '08  
Hello!

Thank you for this useful add-in! I want to add some words about full compatibility with VS2008 . To achieving it you need to do in sources following:
- set to "false" all "Specific Version" properties in all "Microsoft.VisualStudio.DebuggerVisualizers" references;
- set VS_DTE_VERSION = "VisualStudio.DTE". This will be enough for all .NET versions of VS.

After these changes your addin will be successfully working under VS2008 (I'm already using it with VS2oo8).

PS: Could you please add support for Bitmap/Image classes?

Best regards,
Aleksandr.
GeneralRe: VS2008 Compatibility notes
OrlandoCurioso
8:34 25 May '08  
Hi Aleksandr,

Thank you for your very useful comments. I just installed VS2008 and tried it out.

quote:
- set to "false" all "Specific Version" properties ...

On a single VS2008 install in a virtual machine, this worked as you stated. On my box I installed VS2008 side-by-side with VS2005, here I had to specify the 9.0 reference for VS2008, otherwise I get a InvalidCast exception.
quote:
- set VS_DTE_VERSION = "VisualStudio.DTE". This will be enough for all .NET versions of VS.

Good to know and new to me!

quote:
PS: Could you please add support for Bitmap/Image classes?

There are numerous on the web already, see article's Credits section.
I take false pride in publishing few but unique submissions.

Thanks again (rated 5 for your message),
OrlandoCurioso

Try again. Fail again. Fail better. --- Samuel Beckett

AnswerRe: VS2008 Compatibility notes
dzzzen
10:45 25 May '08  
Hi Orlando,

quote:
- set to "false" all "Specific Version" properties ...

On a single VS2008 install in a virtual machine, this worked as you stated. On my box I installed VS2008 side-by-side with VS2005, here I had to specify the 9.0 reference for VS2008, otherwise I get a InvalidCast exception.


I'm no longer vs2005 user, so this is surprize for me.Frown But without this step, VS2008 can't build project, because "invalid reference" error appears. Anyway your precompiled addin works also under VS2008, so this step useful only for peoples who want modify sources under vs2008.

quote:
- set VS_DTE_VERSION = "VisualStudio.DTE". This will be enough for all .NET versions of VS.
Good to know and new to me!


"VisualStudio.DTE" is common ProgID for any VS version and it always (if VS installed) contains reference on CLSID of latest VS version (or latest installed VS - this should be clarified). So be careful. Wink

OrlandoCurioso wrote:
There are numerous on the web already, see article's Credits section.

Yes, I already found all that I need. Wink

Thank you, again!
BR, Aleksandr.
GeneralRe: VS2008 Compatibility notes
Hardy Wang
4:29 8 Feb '09  
Where is that "Specific Version" property? I could not find it anywhere in soruce file.

Thanks!


AnswerRe: VS2008 Compatibility notes
James R. Strawn
10:38 4 Dec '09  
It's the property of the actual reference. So you would use the Solution Explorer and expand the References node. Then right-click on the reference labeled "Microsoft.VisualStudio.DebuggerVisualizers" and select "Properties". One of the properties available is "Specific Version", which, if false, means the resulting assembly is allowed to load any compatible version of this reference it can find. If it's true it means it needs the exact DLL being referenced.

fwiw, I've removed those references and added the VS2008 versions and still no dice. I'm running 2005 and 2008 side by side.

Buses stop at bus stations.
Trains stop at train stations.

My desk has a workstation...

Generalvery nice
geo_m
4:53 12 Mar '08  
very nice idea Wink
thank you
GeneralVS2008
marco_ragogna
23:40 9 Mar '08  
very nice, I had implemented also something similar in the past but very basic, your code is much better.
Can you please post an Add-in for VS2008, I think it would be helpful to many people.
AnswerRe: VS2008
OrlandoCurioso
2:42 10 Mar '08  
Sorry, but in the very near future I won't use VS2008.
Try the compiled dll, it might work, although your settings won't be persisted (different ProgID).
Actually, I hoped that my submission serves as an inspiration, to publish a 'definite', editable visualizer, which benefits us all.

Cheers
OrlandoCurioso
GeneralRe: VS2008
dzzzen
4:40 23 May '08  
Hi,

Please read my message above. You can do a little modifications in sources to achieve VS2008 compatibility.

// BR, Aleksandr.
GeneralWhat a great idea :)
leppie
11:43 8 Mar '08  
I would have never thought of this ever!

Great work! Thanks Smile

xacc.ide - now with IronScheme support
IronScheme - 1.0 alpha 2 out now

GeneralRe: What a great idea :)
Roger Alsing
14:04 8 Mar '08  
Agree, bloody awesome Smile


GeneralRe: What a great idea :)
jomet
0:38 10 Mar '08  
nice work!
many thanks.
Laugh


Last Updated 8 Mar 2008 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010