I am working on a boost.asio based HTTP server. It is supposed to be stopped externally. We use asio signal handling, and it works well for ctrl-c, but does not handle WM_CLOSE, so there is no straightforward way to gracefully close the application externally, e.g. via taskkill. Terminating the process forcibly is not an option. Is there a known approach to this?
-
1Could you try to start your app with `start "MyApp" myapp.exe` and then use `taskkill /FI "WINDOWTITLE eq MyApp*"` to send an event to your app that can be handled with a handler set by `SetConsoleCtrlHandler` (see answer below) ? That way you're killing the containing `cmd.exe` which then sends a `CTRL_CLOSE_EVENT` (IIRC) to your app. – Stefan Näwe Oct 17 '14 at 06:22
-
Thank you! That's very useful too. Is there a straightforward way to make the console hidden (not using third party utilities)? If no, I guess I can write up a trivial launcher, creating a new hidden console for my process. – DmitryShubin Oct 17 '14 at 10:19
-
I was hoping MSVC would map CTRL_CLOSE_EVENT to some signal, so asio can catch it, but apparently this does not happen. Tried SIGINT, SIGTERM, SIGBREAK, SIGABRT, SIGABRT_COMPAT. Any idea? – DmitryShubin Oct 17 '14 at 12:18
-
@DmitryShubin, MSVC maps Ctrl+C to `SIGINT`, and all other events are mapped to `SIGBREAK`. I just tested with a simple `SIGBREAK` handler installed via `signal`. This definitely works to handle `CTRL_CLOSE_EVENT`. The issue is that taskkill.exe will only send `WM_CLOSE` to the effective owner of the console, determined from enumerating windows and calling `GetWindowsThreadProcessId`. Usually this is the process that allocated the console, if it's still running. But closing the console will also kill every process that's attached to it; they get 5 seconds to exit cleanly. – Eryk Sun Feb 15 '18 at 01:30
1 Answers
Update
Just use any IPC method you would "normally" use
Write a simple process control utility that uses e.g.
named_condition
to signal your asio process to shutdown.Note that
named_codition
is somewhat equivalent to a Win32 Named Event in case you think that simpler for this inherently platform specific piece of codeConsider making a Windows Service (NTService), as it looks like this is what you want
- As a long shot, try to create a toplevel window from your process; Either
AllocConsole
orCreateWindow
could help. However, you end up with more platform specific stuff
Original answer, dealing with how to listen for console close events:
This is really not related to using Boost Asio. Of course, on POSIX platforms you could use boost::asio::signal_set
to handle the SIG_INT and SIG_TERM signals.
However, you're on windows, and there is no portable way to detect console close event.
You should write a console handler routine that detects CTRL_CLOSE_EVENT (and CTRL_C_EVENT, if desired), and use SetConsoleCtrlHandler to add the handler routine to your process.
#include <windows.h>
#include <stdio.h>
BOOL CtrlHandler( DWORD fdwCtrlType )
{
switch( fdwCtrlType )
{
// Handle the CTRL-C signal.
case CTRL_C_EVENT:
printf( "Ctrl-C event\n\n" );
Beep( 750, 300 );
return( TRUE );
// CTRL-CLOSE: confirm that the user wants to exit.
case CTRL_CLOSE_EVENT:
Beep( 600, 200 );
printf( "Ctrl-Close event\n\n" );
return( TRUE );
// Pass other signals to the next handler.
case CTRL_BREAK_EVENT:
Beep( 900, 200 );
printf( "Ctrl-Break event\n\n" );
return FALSE;
case CTRL_LOGOFF_EVENT:
Beep( 1000, 200 );
printf( "Ctrl-Logoff event\n\n" );
return FALSE;
case CTRL_SHUTDOWN_EVENT:
Beep( 750, 500 );
printf( "Ctrl-Shutdown event\n\n" );
return FALSE;
default:
return FALSE;
}
}
int main( void )
{
if( SetConsoleCtrlHandler( (PHANDLER_ROUTINE) CtrlHandler, TRUE ) )
{
printf( "\nThe Control Handler is installed.\n" );
printf( "\n -- Now try pressing Ctrl+C or Ctrl+Break, or" );
printf( "\n try logging off or closing the console...\n" );
printf( "\n(...waiting in a loop for events...)\n\n" );
while( 1 ){ }
}
else
{
printf( "\nERROR: Could not set control handler");
return 1;
}
return 0;
}
To coordinate with Boost Asio, you could have the handler stop the (global) io_service object and perhaps set some flag for running actions. Finally, you may have to cancel any async operations in flight (e.g. deadline_timer
s).
Once you did that, it should be pretty clean.

- 374,641
- 47
- 450
- 633
-
Unfortunately, this only works if I press ctrl-c or ctrl-break, but not if I kill it from outside. Specifically, calling taskkill gives this: 'ERROR: The process "term_test.exe" with PID 11656 could not be terminated.' 'Reason: This process can only be terminated forcefully (with /F option).' – DmitryShubin Oct 16 '14 at 15:41
-
Ok, thank you for your suggestions. I agree Windows Service is the proper way to go, but for some reasons our client prefers a console. Will likely go with a window solution. Can you please update your initial reply so it matches the question more closely, and I can accept it? – DmitryShubin Oct 17 '14 at 08:29
-
1@DmitryShubin, there's no reliable way to signal an individual console application in general. But if you know it was started as a process group leader, then you can attach to its console and send Ctrl+Break (i.e. C `SIGBREAK`) to the whole group. If it's not a group leader, that will end up sending Ctrl+Break to every process attached to the console. Also, only one process is the effective owner of the console, to kill without `/F` via `WM_CLOSE`. However, that will also kill all attached processes, since Ctrl+Close event allows 5 seconds to exit cleanly before terminating. – Eryk Sun Feb 15 '18 at 01:23
-
The comment "CTRL-CLOSE: confirm that the user wants to exit" is no longer valid since Vista. Console applications cannot prevent the console from closing. They have 5 seconds to exit gracefully. Then csrss.exe (which injects the control thread on behalf of conhost.exe) will forcibly kill them via `TerminateProcess`, with the exit code `STATUS_CONTROL_C_EXIT`. The handler should do any required cleanup and call `exit` with a better status code if it exited cleanly. – Eryk Sun Feb 15 '18 at 01:36