This program has been developed because the magnifier programs that I found on the Internet were either too expensive for my tastes or were too sparse on features. For many years, I used Workers Collection Magnifying Glass ver. 1.0. I found this to be a wonderful program, but it lacked some features that I wanted. I also found the idea of magnifying the screen intriguing, so I wrote my own program.
I learned how to implement the functions in this program by referencing:
Each of the above programs contributed something beneficial to my understanding of the tools needed to create this program.
An innovative way of managing menus was used in this program. The menu was to remain under the magnifier, but that is not easy to do in .NET, so the menu system was reworked to provide the functionality needed.
This is also my first try at working with global hot keys. The hot key sequence is customizable by the user.
Using the Code
The code is internally documented, so only a brief overview will be given here.
When the program is started, the
OnLoad even will set up various settings and read in user configurations. These settings are saved when the user exits the program in the
Aside from these two routines, there are four regions defined in the code:
- Magnification Routine
- Menu Options
- Hotkey Routines
- Menu Management
These regions each focus on a different part of the program execution.
The Magnification Routines control all the steps necessary to simply magnify the image on the screen and paint it in the form. This is a multiple step process.
tmrRepaint_Tick: First the timer ticks, thereby starting the repaint sequence. The magnifier is moved to the top (unless a submenu is open). The magnifier is moved out of the way of the screen capture. The temporary capture bitmap is created and passed to the next routine.
CaptureDesktop: The temporary bitmap is painted with a capture of the desktop.
CaptureCursor: The current cursor image is captured as another temporary bitmap, and passed directly to the combination routine.
CombineCursorDesktop: The cursor is added to the still un-magnified bitmap of the desktop at the correct location.
RepaintMagnifier: The temporary bitmap, now containing the desktop and cursor, is painted to the form at the correct magnification. The form is cleared first if necessary.
During this process, objects are only deleted and destroyed when the routines won't do it automatically (for simplicity). Additionally, after each repaint operation, garbage collection is run to make sure that the memory doesn't escalate drastically between automated collection runs.
All operations pertaining to the settings and options contained in and configurable by the menu are managed here. There are routines to change the check marks on the menu when one item is checked. There are routines to check to see if the entered values were correct. Options are also set at the execution of these routines.
The hotkey routines simply allow the customization of the global hotkey used to hide and unhide the magnifier. The code for this is found in the project KeyPressDecoder by cjbarth, and also in Managed Windows API.
The routines for menu management were considerably more complicated. In .NET, the context menu will appear above a form in the same application. Which means, in this case, it won't be magnified. In order to overcome this problem, the menu was actually broken down into several menus all related to each other. There isn't a submenu. Using a submenu will hide some of the events and properties that are necessary for processing menus the way I did.
When the menu opens, it sets a global variable with itself and starts a timer. Every time that timer ticks, it checks to see if the mouse is over the menu (
MouseOut). This is not as easy as it seems because there are potential submenus involved here. Additionally, on multiple monitor systems, the
.Contains operation is broken. I could never get it to reliably report if the mouse was actually over the menu or any of its children.
If the mouse is not over the menu, it reduces the opacity by 2%. When the menu 'disappears', it closes.
Since only one submenu can be open at a time, whenever a submenu opens, it changes the global variable to itself. Then, when the mouse-over-menu routines run, they will check both the main menu (as they always do) and then whatever submenu might be open.
Information about which submenu was open (such as
Location) and events about submenu happenings (such as the
Opened event) aren't possible with collection-based submenus of a single context menu. So, multiple context menus were used to get this information. The context menus were then linked together so they appear to be one single menu.
They layout class
clsLayout is where the code is kept that moves the magnifier around and keeps the cursor in the correct position. This code is fairly complex in its design, but easy in its usage.
Basically, what happens here is, every time the box needs to be moved, a call is made to this class to determine what the location should be. This class takes into account such things as the size of the box, the location of the cursor, the proximity of the box to the screen edge, the proximity of the cursor to the box, and the current zoom level. Each of these things has a bearing on the location of the box.
The complexity of these routines stems from the fact that the box isn't stuck to the cursor, but rather the box tries to stay out of the way of the cursor while still remaining near the cursor. Additionally, the box must be in such a position that it isn't magnifying itself, nor must it go off the edge of the screen.
The code in this class is well documented (so I wouldn't confuse myself). For more information, I would suggest reading the code and its comments.
mdlWin32 contains information that pertains to capturing the screen, capturing the cursor, and cleaning up the memory after these operations. System calls such as
GetCursorInfo, along with the structures to hold the return data, are referenced here. In all, calls are made to only two different system DLLs (user32 and gdi32) for a total of seven routines called.
Points of Interest
There is a way to do this entirely in .NET; however, due to bugginess even in version 3.5 of the framework, it doesn't work. There is a method in .NET that would do exactly what we want, called
Graphics.CopyFromScreen. If we were to call that method with
CaptureBlt, we could do this entire repainting operation in one step. Perhaps in .NET 4.0, this will work. For reference, I've included comments to this effect in the code.
- 2009-10-12: Initial release.