Creating a Magnifier in .NET, Part 1: Structure

CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

Introduction

You never know when you may need something. This is the lesson I have learned recently. I am not talking about physical things. I mean, everyone needs money, but this is not what I am talking about. I am talking about a small thing we can take for granted. Eyesight.

Due to an injury, I have been forced to type everything out with mostly one hand, and because of that, the screen and keyboard are farther away than usual. To properly see what I type, I need to make use of the built-in magnifier feature that Windows supplies as an accessibility feature, or I have to set the zoom level or font size higher.

Hence this article.

This article will show you how to make a magnifier with the use of the built-in magnification functions of Windows.

Practical

Open Visual Studio and create a new C# Windows Forms project. Ensure that the form is displayed as Maximized by setting its WindowStyle property.

Add a new class and name it APIMethods. Add the following code into it:

using System;
using System.Runtime.InteropServices;

namespace HTG_Magnify_C
{
   internal static class APIMethods
   {
      public static IntPtr HWND_TOPMOST = new IntPtr(-1);

      public const int USER_TIMER_MINIMUM = 0x0000000A;
      public const int SM_ARRANGE = 0x38;
      public const int SM_CLEANBOOT = 0x43;
      public const int SM_CMONITORS = 80;
      public const int SM_CMOUSEBUTTONS = 0x2b;
      public const int SM_CXBORDER = 5;
      public const int SM_CXCURSOR = 13;
      public const int SM_CXDOUBLECLK = 0x24;
      public const int SM_CXDRAG = 0x44;
      public const int SM_CXEDGE = 0x2d;
      public const int SM_CXFIXEDFRAME = 7;
      public const int SM_CXFOCUSBORDER = 0x53;
      public const int SM_CXFRAME = 0x20;
      public const int SM_CXHSCROLL = 0x15;
      public const int SM_CXHTHUMB = 10;
      public const int SM_CXICON = 11;
      public const int SM_CXICONSPACING = 0x26;
      public const int SM_CXMAXIMIZED = 0x3d;
      public const int SM_CXMAXTRACK = 0x3b;
      public const int SM_CXMENUCHECK = 0x47;
      public const int SM_CXMENUSIZE = 0x36;
      public const int SM_CXMIN = 0x1c;
      public const int SM_CXMINIMIZED = 0x39;
      public const int SM_CXMINSPACING = 0x2f;
      public const int SM_CXMINTRACK = 0x22;
      public const int SM_CXSCREEN = 0;
      public const int SM_CXSIZE = 30;
      public const int SM_CXSIZEFRAME = 0x20;
      public const int SM_CXSMICON = 0x31;
      public const int SM_CXSMSIZE = 0x34;
      public const int SM_CXVIRTUALSCREEN = 0x4e;
      public const int SM_CXVSCROLL = 2;
      public const int SM_CYBORDER = 6;
      public const int SM_CYCAPTION = 4;
      public const int SM_CYCURSOR = 14;
      public const int SM_CYDOUBLECLK = 0x25;
      public const int SM_CYDRAG = 0x45;
      public const int SM_CYEDGE = 0x2e;
      public const int SM_CYFIXEDFRAME = 8;
      public const int SM_CYFOCUSBORDER = 0x54;
      public const int SM_CYFRAME = 0x21;
      public const int SM_CYHSCROLL = 3;
      public const int SM_CYICON = 12;
      public const int SM_CYICONSPACING = 0x27;
      public const int SM_CYKANJIWINDOW = 0x12;
      public const int SM_CYMAXIMIZED = 0x3e;
      public const int SM_CYMAXTRACK = 60;
      public const int SM_CYMENU = 15;
      public const int SM_CYMENUCHECK = 0x48;
      public const int SM_CYMENUSIZE = 0x37;
      public const int SM_CYMIN = 0x1d;
      public const int SM_CYMINIMIZED = 0x3a;
      public const int SM_CYMINSPACING = 0x30;
      public const int SM_CYMINTRACK = 0x23;
      public const int SM_CYSCREEN = 1;
      public const int SM_CYSIZE = 0x1f;
      public const int SM_CYSIZEFRAME = 0x21;
      public const int SM_CYSMCAPTION = 0x33;
      public const int SM_CYSMICON = 50;
      public const int SM_CYSMSIZE = 0x35;
      public const int SM_CYVIRTUALSCREEN = 0x4f;
      public const int SM_CYVSCROLL = 20;
      public const int SM_CYVTHUMB = 9;
      public const int SM_DBCSENABLED = 0x2a;
      public const int SM_DEBUG = 0x16;
      public const int SM_MENUDROPALIGNMENT = 40;
      public const int SM_MIDEASTENABLED = 0x4a;
      public const int SM_MOUSEPRESENT = 0x13;
      public const int SM_MOUSEWHEELPRESENT = 0x4b;
      public const int SM_NETWORK = 0x3f;
      public const int SM_PENWINDOWS = 0x29;
      public const int SM_REMOTESESSION = 0x1000;
      public const int SM_SAMEDISPLAYFORMAT = 0x51;
      public const int SM_SECURE = 0x2c;
      public const int SM_SHOWSOUNDS = 70;
      public const int SM_SWAPBUTTON = 0x17;
      public const int SM_XVIRTUALSCREEN = 0x4c;
      public const int SM_YVIRTUALSCREEN = 0x4d;

      public const string MAGNIFIER = "Magnifier";

      [DllImport("user32.dll", CharSet = CharSet.Auto,
         ExactSpelling = true)]
      public static extern int GetSystemMetrics(int nIndex);

      [DllImport("user32.dll", CharSet = CharSet.Auto,
         ExactSpelling = true)]
      public static extern IntPtr SetTimer(IntPtr hWnd,
         int nIDEvent, int uElapse, IntPtr lpTimerFunc);

      [DllImport("user32.dll", CharSet = CharSet.Auto,
         ExactSpelling = true)]
      [return: MarshalAs(UnmanagedType.Bool)]
      public static extern bool KillTimer(IntPtr hwnd,
         int idEvent);

      [DllImport("user32.dll", CharSet = CharSet.Auto,
         ExactSpelling = true)]
      [return: MarshalAs(UnmanagedType.Bool)]
      public static extern bool GetClientRect(IntPtr hWnd,
         [In, Out] ref RECT rect);

      [DllImport("user32.dll", CharSet = CharSet.Auto,
         ExactSpelling = true)]
      [return: MarshalAs(UnmanagedType.Bool)]
      public static extern bool SetWindowPos(IntPtr hWnd,
         IntPtr hWndInsertAfter, int x, int y, int cx, int cy,
         int flags);

      [DllImport("user32.dll", EntryPoint = "CreateWindowExW",
         CharSet = CharSet.Unicode, CallingConvention =
         CallingConvention.StdCall)]
      public extern static IntPtr CreateWindow(int dwExStyle,
         string lpClassName, string lpWindowName, int dwStyle,
         int x, int y, int nWidth, int nHeight, IntPtr hWndParent,
         IntPtr hMenu, IntPtr hInstance, IntPtr lParam);

      [DllImport("user32.dll", CharSet = CharSet.Auto,
         SetLastError = true, ExactSpelling = true)]
      [return: MarshalAs(UnmanagedType.Bool)]
      public static extern bool SetLayeredWindowAttributes(IntPtr
         hwnd, int crKey, byte bAlpha,
         LayeredWindowAttributeFlags dwFlags);

      [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
      public static extern IntPtr GetModuleHandle([MarshalAs
         (UnmanagedType.LPWStr)] string modName);

      [DllImport("user32.dll", CharSet = CharSet.Auto,
         ExactSpelling = true)]
      [return: MarshalAs(UnmanagedType.Bool)]
      public static extern bool GetCursorPos(ref POINT pt);

      [DllImport("user32.dll", CharSet = CharSet.Auto,
         ExactSpelling = true)]
      [return: MarshalAs(UnmanagedType.Bool)]
      public static extern bool SetCursorPos(int X, int Y);

      [DllImport("user32.dll", CharSet = CharSet.Auto,
         ExactSpelling = true)]
      [return: MarshalAs(UnmanagedType.Bool)]
      public static extern bool InvalidateRect(IntPtr hWnd,
         IntPtr rect, [MarshalAs(UnmanagedType.Bool)] bool erase);

      [DllImport("Magnification.dll", CallingConvention =
         CallingConvention.StdCall)]
      [return: MarshalAs(UnmanagedType.Bool)]
      public static extern bool MagInitialize();

      [DllImport("Magnification.dll", CallingConvention =
         CallingConvention.StdCall)]
      [return: MarshalAs(UnmanagedType.Bool)]
      public static extern bool MagUninitialize();

      [DllImport("Magnification.dll", CallingConvention =
         CallingConvention.StdCall)]
      [return: MarshalAs(UnmanagedType.Bool)]
      public static extern bool MagSetWindowSource(IntPtr hwnd,
         RECT rect);

      [DllImport("Magnification.dll", CallingConvention =
         CallingConvention.StdCall)]
      [return: MarshalAs(UnmanagedType.Bool)]
      public static extern bool MagGetWindowSource(IntPtr hwnd,
         ref RECT pRect);

      [DllImport("Magnification.dll", CallingConvention =
         CallingConvention.StdCall)]
      [return: MarshalAs(UnmanagedType.Bool)]
      public static extern bool MagSetWindowTransform(IntPtr hwnd,
         ref Transform pTransform);

      [DllImport("Magnification.dll", CallingConvention =
         CallingConvention.StdCall)]
      [return: MarshalAs(UnmanagedType.Bool)]
      public static extern bool MagGetWindowTransform(IntPtr hwnd,
         ref Transform pTransform);

      [DllImport("Magnification.dll", CallingConvention =
         CallingConvention.StdCall)]
      [return: MarshalAs(UnmanagedType.Bool)]
      public static extern bool MagSetWindowFilterList(IntPtr hwnd,
         int dwFilterMode, int count, IntPtr pHWND);

      [DllImport("Magnification.dll", CallingConvention =
         CallingConvention.StdCall)]
      public static extern int MagGetWindowFilterList(IntPtr hwnd,
         IntPtr pdwFilterMode, int count, IntPtr pHWND);

      [DllImport("Magnification.dll", CallingConvention =
         CallingConvention.StdCall)]
      [return: MarshalAs(UnmanagedType.Bool)]
      public static extern bool MagSetColorEffect(IntPtr hwnd,
         ref Color pEffect);

      [DllImport("Magnification.dll", CallingConvention =
         CallingConvention.StdCall)]
      [return: MarshalAs(UnmanagedType.Bool)]
      public static extern bool MagGetColorEffect(IntPtr hwnd,
         ref Color pEffect);
   }
}

This class contains definitions and declarations from the Windows Magnification.dll. We import their abilities into our program, so that we can make use of them.

Add a new class and name it APIStructures. Add the following code:

namespace HTG_Magnify_C
{

   internal enum Style : int
   {
      MS_SHOWMAGNIFIEDCURSOR = 0x0001,
      MS_CLIPAROUNDCURSOR = 0x0002,
      MS_INVERTCOLORS = 0x0004
   }


   internal enum Filter
   {
      MW_FILTERMODE_EXCLUDE = 0,
      MW_FILTERMODE_INCLUDE = 1
   }

   internal struct Transform
   {
      public float m00;
      public float m10;
      public float m20;
      public float m01;
      public float m11;
      public float m21;
      public float m02;
      public float m12;
      public float m22;

      public Transform(float Factor)
         : this()
      {
         m00 = Factor;
         m11 = Factor;
         m22 = 1.0f;
      }
   }

   internal struct Color
   {
      public float transform00;
      public float transform10;
      public float transform20;
      public float transform30;
      public float transform40;
      public float transform01;
      public float transform02;
      public float transform03;
      public float transform04;
      public float transform11;
      public float transform12;
      public float transform13;
      public float transform14;
      public float transform21;
      public float transform22;
      public float transform23;
      public float transform24;
      public float transform31;
      public float transform32;
      public float transform33;
      public float transform34;
      public float transform41;
      public float transform42;
      public float transform43;
      public float transform44;
   }

   internal struct POINT
   {
      public int x;

      public int y;

      public POINT(int x, int y)
      {
         this.x = x;
         this.y = y;
      }
   }

   internal struct RECT
   {
      public int left;

      public int top;

      public int right;

      public int bottom;

      public RECT(int left, int top, int right, int bottom)
      {
         this.left = left;
         this.right = right;
         this.top = top;
         this.bottom = bottom;
      }

      public RECT(int width, int height)
      {
         this.left = 0;
         this.top = 0;
         this.right = width;
         this.bottom = height;
      }

      public override bool Equals(object obj)
      {
         RECT r = (RECT)obj;
         return (r.left == left && r.right == right &&
            r.top == top && r.bottom == bottom);
      }

      public override int GetHashCode()
      {
         return ((left ^ top) ^ right) ^ bottom;
      }

      public static bool operator ==(RECT a, RECT b)
      {
         return (a.left == b.left && a.right == b.right &&
            a.top == b.top && a.bottom == b.bottom);
      }

      public static bool operator !=(RECT a, RECT b)
      {
         return !(a == b);
      }

   }

   internal enum WindowStyles : int
   {
      WS_OVERLAPPED = 0x00000000,
      WS_POPUP = -2147483648,
      WS_CHILD = 0x40000000,
      WS_MINIMIZE = 0x20000000,
      WS_VISIBLE = 0x10000000,
      WS_DISABLED = 0x08000000,
      WS_CLIPSIBLINGS = 0x04000000,
      WS_CLIPCHILDREN = 0x02000000,
      WS_MAXIMIZE = 0x01000000,
      WS_CAPTION = 0x00C00000,
      WS_BORDER = 0x00800000,
      WS_DLGFRAME = 0x00400000,
      WS_VSCROLL = 0x00200000,
      WS_HSCROLL = 0x00100000,
      WS_SYSMENU = 0x00080000,
      WS_THICKFRAME = 0x00040000,
      WS_GROUP = 0x00020000,
      WS_TABSTOP = 0x00010000,
      WS_MINIMIZEBOX = 0x00020000,
      WS_MAXIMIZEBOX = 0x00010000
   }

   internal enum CommonWindowStyles : int
   {
      WS_TILED = WindowStyles.WS_OVERLAPPED,
      WS_ICONIC = WindowStyles.WS_MINIMIZE,
      WS_SIZEBOX = WindowStyles.WS_THICKFRAME,
      WS_OVERLAPPEDWINDOW = (WindowStyles.WS_OVERLAPPED |
         WindowStyles.WS_CAPTION | WindowStyles.WS_SYSMENU |
         WindowStyles.WS_THICKFRAME | WindowStyles.WS_MINIMIZEBOX |
         WindowStyles.WS_MAXIMIZEBOX),
      WS_TILEDWINDOW = WS_OVERLAPPEDWINDOW,
      WS_POPUPWINDOW = (WindowStyles.WS_POPUP |
                        WindowStyles.WS_BORDER |
                        WindowStyles.WS_SYSMENU),
      WS_CHILDWINDOW = (WindowStyles.WS_CHILD)
   }

   internal enum SetWindowPosFlags : int
   {
      SWP_NOSIZE = 1,
      SWP_NOMOVE = 2,
      SWP_NOZORDER = 4,
      SWP_NOREDRAW = 8,
      SWP_NOACTIVATE = 0x10,
      SWP_FRAMECHANGED = 0x20,
      SWP_SHOWWINDOW = 0x40,
      SWP_HIDEWINDOW = 0x80,
      SWP_NOCOPYBITS = 0x100,
      SWP_NOOWNERZORDER = 0x200,
      SWP_NOSENDCHANGING = 0x400
   }

   internal enum ExtendedWindowStyles : int
   {
      WS_EX_DLGMODALFRAME = 0x00000001,
      WS_EX_NOPARENTNOTIFY = 0x00000004,
      WS_EX_TOPMOST = 0x00000008,
      WS_EX_ACCEPTFILES = 0x00000010,
      WS_EX_TRANSPARENT = 0x00000020,
      WS_EX_MDICHILD = 0x00000040,
      WS_EX_TOOLWINDOW = 0x00000080,
       WS_EX_WINDOWEDGE = 0x00000100,
      WS_EX_CLIENTEDGE = 0x00000200,
       WS_EX_CONTEXTHELP = 0x00000400,
      WS_EX_RIGHT = 0x00001000,
      WS_EX_LEFT = 0x00000000,
      WS_EX_RTLREADING = 0x00002000,
      WS_EX_LTRREADING = 0x00000000,
      WS_EX_LEFTSCROLLBAR = 0x00004000,
      WS_EX_RIGHTSCROLLBAR = 0x00000000,
      WS_EX_CONTROLPARENT = 0x00010000,
      WS_EX_STATICEDGE = 0x00020000,
      WS_EX_APPWINDOW = 0x00040000,
      WS_EX_LAYERED = 0x00080000,
      WS_EX_NOINHERITLAYOUT = 0x00100000,
      WS_EX_LAYOUTRTL = 0x00400000,
      WS_EX_COMPOSITED = 0x02000000,
      WS_EX_NOACTIVATE = 0x08000000
   }

   internal enum CommonExtendedWindowStyles : int
   {
      WS_EX_OVERLAPPEDWINDOW =
         (ExtendedWindowStyles.WS_EX_WINDOWEDGE |
          ExtendedWindowStyles.WS_EX_CLIENTEDGE),

      WS_EX_PALETTEWINDOW =
         (ExtendedWindowStyles.WS_EX_WINDOWEDGE |
          ExtendedWindowStyles.WS_EX_TOOLWINDOW |
          ExtendedWindowStyles.WS_EX_TOPMOST)
   }

   internal enum LayeredWindowAttributeFlags : int
   {
      LWA_COLORKEY = 0x00000001,
      LWA_ALPHA = 0x00000002
   }


   internal enum LayeredWindowUpdateFlags : int
   {
      ULW_COLORKEY = 0x00000001,
      ULW_ALPHA = 0x00000002,
      ULW_OPAQUE = 0x00000004
   }


   internal enum BlendOperations : byte
   {
      AC_SRC_OVER = 0x00,
      AC_SRC_ALPHA = 0x01
   }

   internal enum ShowWindowStyles : short
   {
      SW_HIDE = 0,
      SW_SHOWNORMAL = 1,
      SW_NORMAL = 1,
      SW_SHOWMINIMIZED = 2,
      SW_SHOWMAXIMIZED = 3,
      SW_MAXIMIZE = 3,
      SW_SHOWNOACTIVATE = 4,
      SW_SHOW = 5,
      SW_MINIMIZE = 6,
      SW_SHOWMINNOACTIVE = 7,
      SW_SHOWNA = 8,
      SW_RESTORE = 9,
      SW_SHOWDEFAULT = 10,
      SW_FORCEMINIMIZE = 11,
      SW_MAX = 11
   }

   internal enum WindowMessage : int
   {
      WM_CREATE = 0x0001,
      WM_DESTROY = 0x0002,
      WM_PAINT = 0x000F,
      WM_CLOSE = 0x0010,
      WM_QUERYENDSESSION = 0x0011,
      WM_QUIT = 0x0012,
      WM_ENDSESSION = 0x0016,
      WM_SETCURSOR = 0x0020,
      WM_MOVE = 0x0003,
      WM_SIZE = 0x0005,
      WM_MOUSEMOVE = 0x0200,
      WM_NCMOUSEMOVE = 0x00A0,
      WM_KEYDOWN = 0x0100,
      WM_SYSKEYDOWN = 0x0104,
      WM_KEYUP = 0x0101,
      WM_CHAR = 0x0102,
      WM_SYSCHAR = 0x0106,
      WM_LBUTTONDOWN = 0x0201,
      WM_LBUTTONUP = 0x0202,
      WM_LBUTTONDBLCLK = 0x0203,
      WM_RBUTTONDOWN = 0x0204,
      WM_RBUTTONUP = 0x0205,
      WM_RBUTTONDBLCLK = 0x0206,
      WM_MBUTTONDOWN = 0x0207,
      WM_MBUTTONUP = 0x0208,
      WM_MBUTTONDBLCLK = 0x0209,
      WM_MOUSEWHEEL = 0x020A,
      WM_MOUSEHOVER = 0x02A1,
      WM_MOUSELEAVE = 0x02A3,
      WM_NCLBUTTONDOWN = 0x00A1,
      WM_NCLBUTTONUP = 0x00A2,
      WM_NCLBUTTONDBLCLK = 0x00A3,
      WM_NCRBUTTONDOWN = 0x00A4,
      WM_NCRBUTTONUP = 0x00A5,
      WM_NCRBUTTONDBLCLK = 0x00A6,
      WM_NCMBUTTONDOWN = 0x00A7,
      WM_NCMBUTTONUP = 0x00A8,
      WM_NCMBUTTONDBLCLK = 0x00A9,
      WM_NCXBUTTONDOWN = 0x00AB,
      WM_NCXBUTTONUP = 0x00AC,
      WM_GETDLGCODE = 0x0087,
      WM_NCHITTEST = 0x0084,
      WM_WINDOWPOSCHANGING = 0x0046,
      WM_WINDOWPOSCHANGED = 0x0047,
      WM_KILLTIMER = 0x402,
      WM_TIMER = 0x113,
      WM_NCPAINT = 0x85,
      WM_ERASEBKGND = 20,
      WM_DROPFILES = 0x233,
      WM_MOUSEACTIVATE = 0x0021,
      WM_ACTIVATE = 0x0006,
      WM_ACTIVATEAPP = 0x001C,
      WM_KILLFOCUS = 8
   }
}

Conclusion

In Part 2, we will put everything together and use it. Until then, happy coding!

Hannes DuPreez
Hannes DuPreez
Ockert J. du Preez is a passionate coder and always willing to learn. He has written hundreds of developer articles over the years detailing his programming quests and adventures. He has written the following books: Visual Studio 2019 In-Depth (BpB Publications) JavaScript for Gurus (BpB Publications) He was the Technical Editor for Professional C++, 5th Edition (Wiley) He was a Microsoft Most Valuable Professional for .NET (2008–2017).

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read