diff --git a/Main.cs b/Main.cs index d7dd5b4..9ca2884 100644 --- a/Main.cs +++ b/Main.cs @@ -340,7 +340,25 @@ namespace winsw try { var proc = Process.GetProcessById(pid); - proc.Kill(); + if (descriptor.SendSIGINT) + { + WriteEvent("Send SIGINT " + process.Id); + bool successful = SigIntHelper.SendSIGINTToProcess(proc); + if (successful) + { + WriteEvent("SIGINT to" + process.Id + " successful"); + } + else + { + WriteEvent("SIGINT to " + process.Id + " failed - Killing as fallback"); + proc.Kill(); + } + } + else + { + WriteEvent("ProcessKill " + process.Id); + proc.Kill(); + } } catch (ArgumentException) { diff --git a/ServiceDescriptor.cs b/ServiceDescriptor.cs index 7453a54..937cb54 100755 --- a/ServiceDescriptor.cs +++ b/ServiceDescriptor.cs @@ -561,5 +561,15 @@ namespace winsw return !string.IsNullOrEmpty(serviceAccountDomain) && !string.IsNullOrEmpty(serviceAccountName); } + /// + /// True if the service can interact with the desktop. + /// + public bool SendSIGINT + { + get + { + return dom.SelectSingleNode("//sendsigint") != null; + } + } } } diff --git a/SigIntHelper.cs b/SigIntHelper.cs new file mode 100644 index 0000000..3378d09 --- /dev/null +++ b/SigIntHelper.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Text; + +namespace winsw +{ + public static class SigIntHelper + { + private const string KERNEL32 = "kernel32.dll"; + + [DllImport(KERNEL32, SetLastError = true)] + private static extern bool AttachConsole(uint dwProcessId); + + [DllImport(KERNEL32, SetLastError = true, ExactSpelling = true)] + private static extern bool FreeConsole(); + + [DllImport(KERNEL32)] + private static extern bool SetConsoleCtrlHandler(ConsoleCtrlDelegate HandlerRoutine, bool Add); + // Delegate type to be used as the Handler Routine for SCCH + private delegate Boolean ConsoleCtrlDelegate(CtrlTypes CtrlType); + + // Enumerated type for the control messages sent to the handler routine + private enum CtrlTypes : uint + { + CTRL_C_EVENT = 0, + CTRL_BREAK_EVENT, + CTRL_CLOSE_EVENT, + CTRL_LOGOFF_EVENT = 5, + CTRL_SHUTDOWN_EVENT + } + + [DllImport(KERNEL32)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool GenerateConsoleCtrlEvent(CtrlTypes dwCtrlEvent, uint dwProcessGroupId); + + /// + /// Uses the native funciton "AttachConsole" to attach the thread to the executing process to try to trigger a CTRL_C event (SIGINT). If the application + /// doesn't honor the event and shut down gracefully, the. wait period will time out after 15 seconds. + /// + /// The process to attach to and send the SIGINT + /// True if the process shut down successfully to the SIGINT, false if it did not. + public static bool SendSIGINTToProcess(Process process) + { + if (AttachConsole((uint)process.Id)) + { + //Disable Ctrl-C handling for our program + SetConsoleCtrlHandler(null, true); + GenerateConsoleCtrlEvent(CtrlTypes.CTRL_C_EVENT, 0); + + process.WaitForExit(15000); + + return process.HasExited; + } + else + { + return false; + } + } + } +} \ No newline at end of file diff --git a/winsw.csproj b/winsw.csproj index cfbb8db..0355d39 100644 --- a/winsw.csproj +++ b/winsw.csproj @@ -3,7 +3,7 @@ Debug AnyCPU - 9.0.21022 + 9.0.30729 2.0 {0DE77F55-ADE5-43C1-999A-0BC81153B039} Exe @@ -56,6 +56,7 @@ +