* Silently ignore INTERROGATE control.
+ * Try to send Control-C events to console applications when
+ shutting them down.
+
Changes since 2.15
-----------------
* Fixed case where NSSM could kill unrelated processes when
Since version 2.15, NSSM is translated into Italian.\r
Thanks Riccardo Gusmeroli.\r
\r
+Since version 2.17, NSSM can try to shut down console applications by\r
+simulating a Control-C keypress. If they have installed a handler routine\r
+they can clean up and shut down gracefully on receipt of the event.\r
+\r
Usage\r
-----\r
In the usage notes below, arguments to the program may be written in angle \r
Thanks to Emilio Frini for spotting that French was inadvertently set as\r
the default language when the user's display language was not translated.\r
Thanks to Riccardo Gusmeroli for Italian translation.\r
+Thanks to Eric Cheldelin for the inspiration to generate a Control-C event\r
+on shutdown.\r
\r
Licence\r
-------\r
Chiamata a GetProcessTimes():
%1
.
+
+MessageId = +1
+SymbolicName = NSSM_EVENT_ATTACHCONSOLE_FAILED
+Severity = Error
+Language = English
+Error attaching to console for service %1.
+AttachConsole() failed:
+%2
+.
+Language = French
+Error attaching to console for service %1.
+AttachConsole() a échoué:
+%2
+.
+Language = Italian
+Error attaching to console for service %1.
+AttachConsole() fallita:
+%2
+.
+
+MessageId = +1
+SymbolicName = NSSM_EVENT_SETCONSOLECTRLHANDLER_FAILED
+Severity = Error
+Language = English
+Error setting null handler for Control-C events sent to service %1.
+SetConsoleCtrlHandler() failed:
+%2
+.
+Language = French
+Error setting null handler for Control-C events sent to service %1.
+SetConsoleCtrlHandler() a échoué:
+%2
+.
+Language = Italian
+Error setting null handler for Control-C events sent to service %1.
+SetConsoleCtrlHandler() fallita:
+%2
+.
+
+MessageId = +1
+SymbolicName = NSSM_EVENT_GENERATECONSOLECTRLEVENT_FAILED
+Severity = Error
+Language = English
+Error generating Control-C event for service %1.
+GenerateConsoleCtrlEvent() failed:
+%2
+.
+Language = French
+Error generating Control-C event for service %1.
+GenerateConsoleCtrlEvent() a échoué:
+%2
+.
+Language = Italian
+Error generating Control-C event for service %1.
+GenerateConsoleCtrlEvent() fallita:
+%2
+.
+
+MessageId = +1
+SymbolicName = NSSM_EVENT_FREECONSOLE_FAILED
+Severity = Warning
+Language = English
+Error detaching from console for service %1.
+FreeConsole() failed:
+%2
+.
+Language = French
+Error detaching from console for service %1.
+FreeConsole() a échoué:
+%2
+.
+Language = Italian
+Error detaching from console for service %1.
+FreeConsole() fallita:
+%2
+.
*/\r
#define NSSM_RESET_THROTTLE_RESTART 1500\r
\r
+/*\r
+ How many milliseconds to wait for the application to die after sending\r
+ a Control-C event to its console.\r
+*/\r
+#define NSSM_KILL_CONSOLE_GRACE_PERIOD 1500\r
/*\r
How many milliseconds to wait for the application to die after posting to\r
its windows' message queues.\r
kill_t k = { pid, exitcode, 0 };
+ /* Try to send a Control-C event to the console. */
+ if (! kill_console(service_name, process_handle, pid)) return 1;
+
/*
Try to post messages to the windows belonging to the given process ID.
If the process is a console application it won't have any windows so there's
return TerminateProcess(process_handle, exitcode);
}
+/* Simulate a Control-C event to our console (shared with the app). */
+int kill_console(char *service_name, HANDLE process_handle, unsigned long pid) {
+ unsigned long ret;
+
+ /* Try to attach to the process's console. */
+ if (! AttachConsole(pid)) {
+ ret = GetLastError();
+
+ switch (ret) {
+ case ERROR_INVALID_HANDLE:
+ /* The app doesn't have a console. */
+ return 1;
+
+ case ERROR_GEN_FAILURE:
+ /* The app already exited. */
+ return 2;
+
+ case ERROR_ACCESS_DENIED:
+ default:
+ /* We already have a console. */
+ log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_ATTACHCONSOLE_FAILED, service_name, error_string(ret), 0);
+ return 3;
+ }
+ }
+
+ /* Ignore the event ourselves. */
+ ret = 0;
+ if (! SetConsoleCtrlHandler(0, TRUE)) {
+ log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETCONSOLECTRLHANDLER_FAILED, service_name, error_string(GetLastError()), 0);
+ ret = 4;
+ }
+
+ /* Sent the event. */
+ if (! ret) {
+ if (! GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0)) {
+ log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GENERATECONSOLECTRLEVENT_FAILED, service_name, error_string(GetLastError()), 0);
+ ret = 5;
+ }
+ }
+
+ /* Detach from the console. */
+ if (! FreeConsole()) {
+ log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_FREECONSOLE_FAILED, service_name, error_string(GetLastError()), 0);
+ }
+
+ /* Wait for process to exit. */
+ if (! WaitForSingleObject(process_handle, NSSM_KILL_CONSOLE_GRACE_PERIOD)) return 6;
+
+ return ret;
+}
+
void kill_process_tree(char *service_name, unsigned long pid, unsigned long exitcode, unsigned long ppid, FILETIME *parent_creation_time, FILETIME *parent_exit_time) {
/* Shouldn't happen unless the service failed to start. */
if (! pid) return;
int CALLBACK kill_window(HWND, LPARAM);
int kill_threads(char *, kill_t *);
int kill_process(char *, HANDLE, unsigned long, unsigned long);
+int kill_console(char *, HANDLE, unsigned long);
void kill_process_tree(char *, unsigned long, unsigned long, unsigned long, FILETIME *, FILETIME *);
#endif