Introduction
I wrote this article, not only as a computer engineer I am, but also as a father I became of two marvelous children I have. I have always turned on the computer and my children try to use it by clicking the mouse, and striking, smashing and smiting the keyboard. Often, they push the 'Windows' key or the 'Esc' key, or some other key that finishes the execution of some programs, starts applications, etc� That is the reason why I developed this little tool that allows my 'marvelous beasts' to hit the keys they want and the only thing that occurs is that an image will be changed.
What this application does is, cancels all the keyboard events except:
- Ctrl+Alt+Del Captured by OS and
- Ctrl+Alt+End used for finishing the application.
All the other events will be captured and ignored. On the other hand, it will show an image randomly taken from the images stored in the directory Images, and this image will change every certain number of seconds defined in the application. Also, it will change the mouse cursor with an icon randomly taken from the directory Images every certain number of seconds or every 100 mouse move events.
Before executing the demo
Before executing the demo, we must get some BMPs and ICOs and put them into the Images directory.
Using the code
The application is very simple, and here I only present a minimal version just for maintaining the code clean and easy to read. There are three classes:
CForTheKidsApp
: the application class, created by the environment.
CForTheKidsDlg
: the dialog class that will be shown and having the control-view.
CImageLoader
: an auxiliary class that will help us find what images to load and load it into a static control, cursor, etc�
The main function that is called is in the initialization of the dialog (CForTheKidsDlg::OnInitDialog()
) and it will do the Hook to the keyboard. This hook procedure will monitor low-level keyboard input events. As the procedure that will be called is a global procedure, and we will need the hook itself, we declare a global variable called TheKeyHook
that will have the HHOOK
handle. For doing that, we execute the following statement:
TheKeyHook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, ppI,0);
We want that the LowLevelKeyboardProc
will filter all the keyboard events except one combination that will let us finish the application. We decide that this combination could be Ctrl+Alt+End because it is a combination that a child rarely can execute because of the separation that exists between those keys. This procedure must return 1
if we want that the event be ignored.
That is the reason that if the code is a HC_ACTION
then we always return 1
, except for the Ctrl+Alt+End combination in which case we exit the application. For more information about this, see LowLevelKeyboardProc in MSDN.
It is important to remark that before we exit the application, we must unhook the keyboard event and send a message to the main window to close, using the following sentences:
UnhookWindowsHookEx(TheKeyHook);
SendMessage(TheMainWndHnd,WM_CLOSE,0,0);
The LowLevelKeyboardProc
procedure will be the following:
LRESULT CALLBACK LowLevelKeyboardProc(INT nCode, WPARAM wParam, LPARAM lParam)
{
try
{
KBDLLHOOKSTRUCT *pkbhs = (KBDLLHOOKSTRUCT *) lParam;
int error=GetLastError();
switch (nCode)
{
case HC_ACTION:
{
BOOL bControlKeyDown =
GetAsyncKeyState (VK_CONTROL) >> ((sizeof(SHORT) * 8) - 1);
if (pkbhs->vkCode == VK_END &&
LLKHF_ALTDOWN && bControlKeyDown)
{
UnhookWindowsHookEx(TheKeyHook);
SendMessage(TheMainWndHnd,WM_CLOSE,0,0);
}
else
return 1;
break;
}
default:
break;
}
CallNextHookEx (TheKeyHook, nCode, wParam, lParam);
}
catch(...)
{
}
return 0;
}
Another important thing that it is not explained directly in most sites is that if you want to use the WH_KEYBOARD_LL
flag, you must have the _WIN32_WINNT
equal or greater than 1024 (0x0400). For doing that, we must include into the settings the following Preprocessor definition:
_WIN32_WINNT=1024
Special points to consider
- The images are changed using a timer, initially set with the defines
_TIMER_ELAPSE_BMP_
and _TIMER_ELAPSE_ICO_
.
- The timers have the identifiers defined with
_TIMER_CHANGEBMP_
and _TIMER_CHANGEICO_
.
- The images are taken from the directory Images placed where the application is executed.
Ideas for improving the application
- The code, as is, is just the beginning of what this program can do. I only change the image every N milliseconds, but we can add an
OnLButtonDown
event, an OnRButtonDown
event, and we can use the hook for intercepting some keys that we would want to have some special behavior.
- It only reads .bmp and .ico files because it is not the aim of this tool to read all kinds of images, and adding this functionality will only complicate the reading of the code. If you want, you can easily add other kinds of images into the
CImageLoader
class.
- The image directory, elapsed time, etc� are hard-coded, but it can be easily extracted and put into an .ini file.
- If we want, we can use this tool to teach numbers and letters to our children. For instance, we show an image of the letter P and we want that the child press that key. If he or she does that, we can show a beautiful image or play a nice music. They can investigate all the keys, and those keys we do not want them to touch, we can easily 'cancel' from the system.
- In my computer, I download lots of images of bulldozers and trains, and every time my child smashes the keyboard, another train/bulldozer appears and he has fun!
Remarks for debugging
- If we debug the application, we must not use the hook, because the keyboard will be ignored, and if a breakpoint is set, the application will stop and the keyboard 'hangs'. In this case, we must finish the IDE, losing all the data not saved and, often, we must restart the computer.
- The main dialog
CForTheKidsDlg
, has the System modal
property enabled, which prohibits switching to another window or program while the dialog box is active. Take care while debugging to set it to false allowing the debugger to be switched.
History
Minimal version.
Version 1.0 with memory leak solved, thanks to VaKa.