I have some problems with the handling of CTRL+C events, in a Win32 C++ console program.
Basically my program looks like this: (based on this other question: Windows Ctrl-C - Cleaning up local stack objects in command line app)
bool running;
int main() {
running = true;
SetConsoleCtrlHandler((PHANDLER_ROUTINE) consoleHandler, TRUE);
while (running) {
// do work
...
}
// do cleanup
...
return 0;
}
bool consoleHandler(int signal) {
if (signal == CTRL_C_EVENT) {
running = false;
}
return true;
}
The problem is the cleanup code not being executed at all.
After the execution of the handler function the process is terminated, but without execute the code after the main loop. What's wrong?
EDIT: as requested, this is a minimal test case similar to my program: http://pastebin.com/6rLK6BU2
I don't get the "test cleanup-instruction" string in my output.
I don't know if this is important, I'm compiling with MinGW.
EDIT 2: The problem with the test case program is the use of the Sleep() function. Without it the program works as expected.
In Win32 the function handler runs in another thread, so when the handler/thread ends its execution the main thread is sleeping. Probably this is the cause of process interruption?
The following code works for me:
#include <windows.h>
#include <stdio.h>
BOOL WINAPI consoleHandler(DWORD signal) {
if (signal == CTRL_C_EVENT)
printf("Ctrl-C handled\n"); // do cleanup
return TRUE;
}
int main()
{
running = TRUE;
if (!SetConsoleCtrlHandler(consoleHandler, TRUE)) {
printf("\nERROR: Could not set control handler");
return 1;
}
while (1) { /* do work */ }
return 0;
}
According to the documentation, when the handler (which is declared wrong, BTW) receives a CTRL_CLOSE_EVENT, CTRL_LOGOFF_EVENT, or CTRL_SHUTDOWN_EVENT signal, the process is terminated after the handler exits. To do what you are attempting, you are supposed to move your cleanup code into the handler itself.
Depending on your specific requirements you have a number of options. If you simply want to ignore Ctrl+C you can call SetConsoleCtrlHandler passing NULL as the HandlerRoutine parameter:
int _tmain(int argc, _TCHAR* argv[])
{
SetConsoleCtrlHandler(NULL, TRUE);
// do work
return 0;
}
This removes all signal handlers. To terminate this application you have to implement custom logic to determine when to shut down.
If you want to handle Ctrl+C you have two options: Set up a handler for the signal or pass the keyboard input on to regular keyboard handling.
Setting up a handler is similar to the code above, but instead of passing NULL as the handler you provide your own implementation.
#include <windows.h>
#include <stdio.h>
volatile bool isRunnung = true;
BOOL WINAPI HandlerRoutine(_In_ DWORD dwCtrlType) {
switch (dwCtrlType)
{
case CTRL_C_EVENT:
printf("[Ctrl]+C\n");
isRunnung = false;
// Signal is handled - don't pass it on to the next handler
return TRUE;
default:
// Pass signal on to the next handler
return FALSE;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
SetConsoleCtrlHandler(HandlerRoutine, TRUE);
printf("Starting\n");
while ( isRunnung ) {
Sleep(0);
}
printf("Ending\n");
return 0;
}
The output of this application is:
Starting
[Ctrl]+C
Ending
Note that cleanup code is executed, regardless of the code inside the main while-loop. Signal handlers form a linked list, where the handler functions are called on a last-registered, first-called basis until one of the handlers returns TRUE. If none of the handlers returns TRUE, the default handler is called. The default handler for a console calls ExitProcess when processing Ctrl+C.
If you want to prevent any preprocessing and handle Ctrl+C as regular keyboard input instead you have to change the console mode by calling SetConsoleMode.
#include <windows.h>
#include <stdio.h>
int _tmain(int argc, _TCHAR* argv[])
{
DWORD dwMode = 0x0;
GetConsoleMode( GetStdHandle(STD_INPUT_HANDLE), &dwMode );
// Remove ENABLE_PROCESSED_INPUT flag
dwMode &= ~ENABLE_PROCESSED_INPUT;
SetConsoleMode( GetStdHandle(STD_INPUT_HANDLE), dwMode );
while ( true ) {
// Ctrl+C can be read using ReadConsoleInput, etc.
}
return 0;
}
Once the ENABLE_PROCESSED_INPUT flag is removed Ctrl+C is no longer processed by the system and passed to the console like regular keyboard input. It can be read using ReadConsoleInput or ReadFile.
Disclaimer: The above has been tested on Windows 8 64bit, compiled for 32 and 64 bit, Release and Debug configurations.
来源:https://stackoverflow.com/questions/18291284/handle-ctrlc-on-win32