Creating a Magnifier in .NET, Part 1: Structure

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