Introduction
Everyone programs console applications one time or the other. This
programming is more prevalent when people are learning to program, especially
while learning the DOS based C/C++ programming. However, when one migrates to
Windows programming, console application development takes a back seat. But the
Win32 console development holds an important place, especially when the Win32
API contains a good amount of API dedicated to console application development.
If you have noticed, even VC++, and latest development technologies like C#,
also supports console project development. Console applications are good
candidates for testing the core functionality of your Windows application
without the unnecessary overhead of a GUI.
But there's always been a sense of helplessness in regard to how to know when
certain system related events have occurred, like when user if logging off, or
the system is being shutdown, or handling control+break or control+C keyboard
events, etc. For a Windows based application, getting to know when such events
occur is no problem since they are having a message queue assigned to them that
is polled, and assuming that the concerned event is programmed for, it can be
handled pretty easily. But this isn't the case with a console application that
has no concept of a message queue.
This article intends to discuss how you can handle all kinds of console-based
events in any console application. Once you have gone through it, you will see
for yourself how trivial this seemingly helpless task is :)
Setting Console Traps
The first step in handling console application events is to setup an even
trap, technically referred to as installing an event handler. For this purpose,
we utilize the SetConsoleCtrlHandler
Win32 API that is prototyped as shown
below:
BOOL SetConsoleCtrlHandler(
PHANDLER_ROUTINE HandlerRoutine,
BOOL Add
);
The HandlerRoutine
parameter is a pointer to a function that has the
following prototype:
BOOL WINAPI HandlerRoutine(
DWORD dwCtrlType
);
All the HandlerRoutine
takes is a DWORD
parameter that tells what console
event has taken place. The parameter can take the following values:
CTRL_C_EVENT
- occurs when the user presses CTRL+C, or when it is sent by
the GenerateConsoleCtrlEvent
API.
CTRL_BREAK_EVENT
- occurs when the user presses CTRL+BREAK, or when it is
sent by the GenerateConsoleCtrlEvent
API.
CTRL_CLOSE_EVENT
- occurs when attempt is made to close the console, when
the system sends the close signal to all processes associated with a given
console.
CTRL_LOGOFF_EVENT
- occurs when the user is logging off. One cannot
determine, however, which user is logging off.
CTRL_SHUTDOWN_EVENT
- occurs when the system is being shutdown, and is
typically sent to services.
Upon receiving the event, the HandlerRoutine
can either choose to do some
processing, or ignore the event. If the routine chooses not to handle the event,
it should return FALSE
, and the system shall then proceed to the next installed
handler. But incase the routine does handle the event, it should then return
TRUE
, after doing all the processing it requires. The CTRL_CLOSE_EVENT
,
CTRL_LOGOFF_EVENT
and CTRL_SHUTDOWN_EVENT
are typically used to perform any
cleanup that is required by the application, and then call the ExitProcess
API.
Thus, the system has has some timeouts associated with these three events, which
is 5 seconds for CTRL_CLOSE_EVENT
, and 20 seconds for the other two. If the
process doesn't respond within the timeout period, Windows shall then proceed to
display the End Task dialog box to the user. If the user proceeds to end the
task, then the application will not have any opportunity to perform cleanup.
Thus, any cleanup that is required should complete well within the timeout
period. Below is an exemplification of the handler routine:
BOOL WINAPI ConsoleHandler(DWORD CEvent)
{
char mesg[128];
switch(CEvent)
{
case CTRL_C_EVENT:
MessageBox(NULL,
"CTRL+C received!","CEvent",MB_OK);
break;
case CTRL_BREAK_EVENT:
MessageBox(NULL,
"CTRL+BREAK received!","CEvent",MB_OK);
break;
case CTRL_CLOSE_EVENT:
MessageBox(NULL,
"Program being closed!","CEvent",MB_OK);
break;
case CTRL_LOGOFF_EVENT:
MessageBox(NULL,
"User is logging off!","CEvent",MB_OK);
break;
case CTRL_SHUTDOWN_EVENT:
MessageBox(NULL,
"User is logging off!","CEvent",MB_OK);
break;
}
return TRUE;
}
Now that we have seen how the handler routine works, lets see how to install
the handler. To do so, as mentioned earlier in the article, we use the
SetConsoleCtrlHandler
API as shown below:
if (SetConsoleCtrlHandler(
(PHANDLER_ROUTINE)ConsoleHandler,TRUE)==FALSE)
{
printf("Unable to install handler!\n");
return -1;
}
The first parameter is a function pointer of the type PHANDLER_ROUTINE
, whose
prototype has been discussed earlier. The second parameter, if set to TRUE
,
tries installing the handler, and if set to FALSE
, attempts the un-installation.
If either attempts are successful, the return value is TRUE
. Otherwise
FALSE
is
returned.
So, that's all there is to handling the console application events. After
handler is installed, your application will receive the events as and by they
come, and when the execution is about to be terminated, the handler maybe
un-installed. Pretty easy, eh :) ?