
Introduction
The mouse was, and still is, one of the greatest inventions in the field of personal computing and is of great help especially in a graphical user environment. Do you know who invented it? It was Dr. Douglas C. Engelbart. He also invented many other very useful stuff like teleconferencing, email, the internet etc. to name a few. He has many patents to his name and his name has also been inducted into the National Inventors Hall of Fame. I bet that less than 1% of all the mouse users around the world know about this great guy. Neither did I, before I started out to write this article.
Anyway, what matters is that more than 95% of the world�s population knows about the computer mouse. And I�m sure you could not have reached here to read this article without using it. But to some people, including me, if you are familiar with a particular environment or application, navigation or working in it is much easier and faster using the keyboard than the mouse. This is possible mainly due to the keyboard shortcuts provided by the application, which you learn by heart as you gain more experience.
Certain versions of the Microsoft Windows operating system like Windows NT 4.0, Windows 2000 and Windows XP etc., provides an option to automatically move the mouse pointer to the default button in a dialog box when it first appears on the screen. This settings is shown in the figure below �

I found the above-mentioned feature of windows to be very useful, as I had to move the mouse much less than when that option was not checked. But this proved to be a real hindrance when I was working with only the keyboard, like when typing some documentation or when coding, because every time I press some keyboard shortcut, a dialog would pop up that would move the mouse pointer to its default button, leaving the mouse pointer in the client area of the application. This posed to be a real problem especially when the cursor was some big animated cursor that kept rotating in front of your eyes (I had a rotating fender guitar as my cursor).
I had a lot of options to overcome the above-mentioned issue. I could uncheck the �Snap to default� checkbox or I could change my cursor to the normal arrow cursor or I could manually move the mouse whenever it jumped up on the screen. But I thought of doing something else so that I could have the features and cursors that I liked sans the hindrance. I wrote a program that works in the background, which moves the mouse pointer to a specified location after a specified amount of mouse idle time.
Running the demo application
The basic technique used is installing a system wide mouse hook. In general, to install a system wide Windows hook, the hook procedure must be written in a separate DLL file and that DLL must be invoked from another executable.
The sample workspace consists of two projects. One is the DLLs project and the other is the executables project. To compile and run the sample code, first set the DLLs project as the active project and build. Then, set the EXEs project as the active project, build and run. Both the projects are written only using the windows SDK and no other libraries like MFC, ATL etc. are used except for the default runtime library. The DLL is implicitly linked with the EXE, i.e., the DLL is loaded into memory along with the EXE and stays in memory till the EXE is exited.
When the code is run a tray icon appears which is the same as the mouse cursor. Right clicking on the tray icon brings up a menu. The tray icon and the menu is shown in the figure below �

Settings: This option brings up the dialog shown in the figure below. The �Time� field specifies the mouse idle time in seconds after which the mouse must be parked. The �X Position� and �Y Position� specifies the screen coordinates where the mouse must be parked.

Park Cursor: This option simply moves the cursor to the specified location in the �X Position� and �Y Position� fields.
Suspend: This option when checked will stop the mouse from being parked until the option is unchecked.
Stop Parking: This option will uninstall the system wide mouse hook and exit the application.
How to hook
The idle time of the mouse must be checked at all times irrespective of the application that you are working on. This necessitates the installation of a system wide hook. System wide hooks in windows must be written as a separate DLL.
When installing the hook using the SetWindowsHookEx
API, we specify a function that handles all the events of that particular hook. This function is called the filter function. The operating system maintains a list or stack of such filter functions. When a new hook is installed, the filter function of the hook is added as the first function in the list. To ensure that the previous filter function in the list is called, the CallNextHookEx
API must be called from within our filter function.
Also to share the values of certain variables across processes, the variables must be declared in a shared data section. The shared data section can be created by first declaring a new data section using the #pragma data_seg segment_name
directive and then stating that the section is shared using segment_name SHARED
in the .DEF file.
Understanding the sample code
The DLL in the sample application is a very simple DLL written in SDK, which exports 5 functions �
InstallHook
- Calls the SetWindowsHookEx
API, which actually installs the system wide mouse hook. It also creates a timer with a time-out value of 1 second.
extern "C" void InstallHook()
{
if(s_hHook)
return;
s_hHook = SetWindowsHookEx(WH_MOUSE, MouseProc, g_hInst, 0);
if(NULL == s_hHook)
MessageBox(0, "Hook Failed", "", 0);
g_nTimer = SetTimer(NULL, NULL, 1000, TimerProc);
}
UninstallHook
- Calls the UnhookWindowsHookEx
API, which uninstalls the system wide mouse hook. It also kills the timer created in the InstallHook
function.
extern "C" void UninstallHook()
{
if(s_hHook)
{
KillTimer(NULL, g_nTimer);
UnhookWindowsHookEx(s_hHook);
s_hHook = NULL;
}
}
SetValues
- Sets the mouse idle time, the X screen coordinate and the Y screen coordinate, which are passed as parameters to the functions to global variables.
extern "C" void SetValues(int* lCount, int* lXPos, int* lYPos)
{
if(*lCount > 1)
g_lCount = *lCount;
if(*lXPos >= 0)
g_lXPos = *lXPos;
if(*lYPos >= 0)
g_lYPos = *lYPos;
*lCount = g_lCount;
*lXPos = g_lXPos;
*lYPos = g_lYPos;
}
Suspend
- Toggles a flag ON and OFF.
extern "C" bool Suspend()
{
s_bSuspend ^= 1;
return s_bSuspend;
}
ParkCursor
- Calls the SetCursorPos
API, which actually moves the mouse to the specified screen coordinates.
extern "C" void ParkCursor()
{
SetCursorPos(g_lXPos, g_lYPos);
}
The hook needs to maintain certain variables that must be accessible across processes. Putting the necessary variables into a shared data segment does this. It does this by first declaring a data section by the name �ParkMouse� as shown below �
#pragma data_seg("ParkMouse")
HHOOK s_hHook = 0;
int s_lCount = 0;
int g_lCount = 5;
int g_lXPos = 3;
int g_lYPos = 3;
bool s_bSuspend = 0;
#pragma data_seg()
This new data section is made readable, writable and sharable by giving ParkMouse READ WRITE SHARED
in the SECTIONS
section of the .DEF file.
The working of the hook is very simple. The name of our filter function is MouseProc
. All this function does is call the CallNextHookEx
API and if any mouse event has occurred, set the variable s_lCount
to 0.
LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam)
{
LRESULT lResult = CallNextHookEx(s_hHook, nCode, wParam, lParam);
if(HC_ACTION == nCode)
s_lCount = 0;
return lResult;
}
The actual work of moving the mouse when the specified idle time expires happens in the TimerProc
function which is called by the system every second. This happens because we had called the SetTimer
API with a time-out value of 1 second from the InstallHook
function. Each time the TimerProc
function is called, the value of s_lCount
is incremented by 1. If the values of s_lCount
and g_lCount
become equal, the mouse is moved using the SetCursorPos
API to the location specified in g_lXPos
and g_lYPos
. Any of the mouse buttons currently down is also checked before moving the mouse. If the suspend flag is set, the TimerProc
function does nothing and simply returns.
void CALLBACK TimerProc(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime)
{
if(s_bSuspend)
return;
s_lCount++;
if(g_lCount == s_lCount)
{
if(GetAsyncKeyState(VK_LBUTTON) >= 0 &&
GetAsyncKeyState(VK_MBUTTON) >= 0 &&
GetAsyncKeyState(VK_RBUTTON) >= 0)
{
SetCursorPos(g_lXPos, g_lYPos);
}
}
}
The executable that loads the DLL into memory in the first place is a simple program also written in SDK, which creates a tray icon and a hidden window to process the messages from the tray icon. The tray icon is the same as your default cursor. This is done by loading the default cursor using LoadCursor(NULL, IDC_ARROW)
and passing the returned handle as the handle to the icon of the tray.
The dialog box that pops up on clicking the �Settings� menu from the tray is not created using a dialog template resource, but is created dynamically using many CreateWindow
API calls in the CreateSettingsDialog
function. Why did I do that? Well, I had nothing better to do, that�s why.
void CreateSettingsDialog()
{
g_hWnd = CreateWindowEx(WS_EX_DLGMODALFRAME, "Park",
"Park Mouse - Settings", WS_POPUPWINDOW|WS_CAPTION,
0, 0, DLGWIDTH, DLGHEIGHT, 0, 0, hInst, 0);
g_hTime = CreateWindow("STATIC", 0, WS_CHILD|WS_VISIBLE,
10, 10, 301, 18, g_hWnd, 0, hInst, 0);
SetWindowText(g_hTime, "Time : seconds "
"(Must be more than 1)");
g_hTime = CreateWindow("STATIC", 0, WS_CHILD|WS_VISIBLE,
17, 38, 80, 18, g_hWnd, 0, hInst, 0);
SetWindowText(g_hTime, "X Position :");
g_hTime = CreateWindow("STATIC", 0, WS_CHILD|WS_VISIBLE,
167, 38, 80, 18, g_hWnd, 0, hInst, 0);
SetWindowText(g_hTime, "Y Position :");
g_hTime = CreateWindow("STATIC", 0,
SS_ETCHEDFRAME|WS_CHILD|WS_VISIBLE, 5, 5, 311, 28,
g_hWnd, 0, hInst, 0);
g_hTime = CreateWindow("STATIC", 0,
SS_ETCHEDFRAME|WS_CHILD|WS_VISIBLE, 5, 31, 311, 28,
g_hWnd, 0, hInst, 0);
g_hOK = CreateWindow("BUTTON", 0, WS_CHILD|WS_VISIBLE,
70, 70, 80, 25, g_hWnd, 0, hInst, 0);
SetWindowText(g_hOK, "Apply");
g_hCancel = CreateWindow("BUTTON", 0, WS_CHILD|WS_VISIBLE,
190, 70, 80, 25, g_hWnd, 0, hInst, 0);
SetWindowText(g_hCancel, "Close");
g_hTime = CreateWindow("EDIT", 0,
WS_BORDER|WS_CHILD|WS_VISIBLE, 56, 10, 50, 18,
g_hWnd, 0, hInst, 0);
g_hXPos = CreateWindow("EDIT", 0,
WS_BORDER|WS_CHILD|WS_VISIBLE, 98, 37, 50, 18,
g_hWnd, 0, hInst, 0);
g_hYPos = CreateWindow("EDIT", 0,
WS_BORDER|WS_CHILD|WS_VISIBLE, 248, 37, 50, 18,
g_hWnd, 0, hInst, 0);
}
Conclusion
I agree that it is a stupid application. But I hope it will be a useful introduction to system wide Windows hooks for people who don�t know about it. Then again, I guess it is a fancy application. And hey, I bet most of you didn�t know about that great inventor.