A .NET Implementation of the ZoomIn Utility

ZoomIn Utility in Action

The ZoomIn Utility enables you to look at any area of the screen and capture the display. This can be an excellent way to snip graphics from other applications or to work out the exact details of how other controls are drawn. A copy of this is provided along with Visual Studio as a tool and also as sample MFC source code from Microsoft, so it is instructive to see how much easier this is to do in .NET. As it is easier, this example provides a number of enhancements, such as Saving, Printing, Gridlines and a StatusBar for pixel position and RGB values.

Zoom-In Basics

The idea of ZoomIn is to allow the user to select any area of the desktop and to display it within the ZoomIn window, where it can be zoomed up to 32x its original size. In order to do this you need to be able to create a UI which allows you to select a point on the desktop and one which works out the contents. This part of the application is slightly beyond the .NET framework as it doesn't contain any wrapper for getting at the desktop itself. However, the System.Drawing implementation is based on GDI+ and contains a number of methods which make it easy to interoperate with a handful of Unmanaged GDI methods to perform this.

Drawing A Selection Rectangle

To draw a selection rectangle on the desktop, we take advantage of the fact that if you XOR the same bits with any pattern twice you end up with the same result. For example:

    // XOR once:
    0x6A ^ 0xF0 = 0x9A
    // XOR again:
    0x9A ^ 0xF0 = 0x6A

In a bitmap on a display, if the colour you are XORing with is black then the effect of the XOR is similar to inverting the image. This is how almost all drag rectangles are drawn in Win32 applications. Rather unfortunately, the concept of XOR drawing is not present at all in .NET (neither is it present in GDI+ for that matter). In any case, GDI allows us to perform XOR drawing through the BitBlt, StretchBlt and PatBlt methods. In this case I am using PatBlt, which paints a rectangle using the brush currently selected into a device context, combining the brush color with the surface color using a number of raster operations.

So to draw the selection rectangle, all we need to do is to draw the four edges of the rectangle using the XOR operation (which is known as DSTINVERT in the GDI API) and then remember where it was last drawn. To erase the previous rectangle, just draw the exact same image.

Working with the Desktop Device Context

Windows allows you to work with the Desktop through its device context, which is provided as a handle. To get the desktop handle, use the GDI GetDC method, passing in 0 as the window handle to get the DC for. Once you've finished working with a DC returned by the GetDC method you need to release it again using ReleaseDC, otherwise Windows keeps allocating resources until it runs out of memory. These methods are equivalent to the System.DrawingGetHdc and ReleaseHdc methods, except that System.Drawing complains more if you don't perform the operations in pairs, which is probably a good thing.

Once you have a device context, the simplest way to transfer the information to the Managed world is to use the GDI StretchBlt (or BitBlt) methods to copy the data to a hdc obtained from a System.Drawing.Graphics object.

Putting it Together

Once this small amout of Unmanaged code is in place, the other features of the ZoomIn application are trivial to implement. The standard System.Windows.Forms.Form mouse events can be used to scroll the selection window around. System.Drawing features can be used to create copies of the image for pixel interrogation, saving and copying to the clipboard.