public static async Task <bool> TrySignalAsync(int processId, NativeMethods.CtrlType signal) { if (HasSameConsole(processId)) { return(await SendSignalFromCurrentProcess(processId, signal).ConfigureAwait(false)); } using (var file = await DeploySignalerExeAsync().ConfigureAwait(false)) { var command = Command.Run(file.Path, new object[] { processId, (int)signal }); return((await command.Task.ConfigureAwait(false)).Success); } }
private static async Task <bool> SendSignalFromCurrentProcess(int processId, NativeMethods.CtrlType signal) { await SignalFromCurrentProcessLock.WaitAsync().ConfigureAwait(false); try { using (var waitForSignalSemaphore = new SemaphoreSlim(initialCount: 0, maxCount: 1)) { NativeMethods.ConsoleCtrlDelegate handler = receivedSignal => { if (receivedSignal == signal) { waitForSignalSemaphore.Release(); // if we're signaling another process on the same console, we return true // to prevent the signal from bubbling. If we're signaling ourselves, we // allow it to bubble since presumably that's what the caller wanted return(processId != ProcessHelper.CurrentProcessId); } return(false); }; if (!NativeMethods.SetConsoleCtrlHandler(handler, add: true)) { Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); } try { if (!NativeMethods.GenerateConsoleCtrlEvent(signal, NativeMethods.AllProcessesWithCurrentConsoleGroup)) { return(false); } // Wait until the signal has reached our handler and been handled to know that it is safe to // remove the handler. // Timeout here just to ensure we don't hang forever if something weird happens (e. g. someone // else registers a handler concurrently with us). return(await waitForSignalSemaphore.WaitAsync(TimeSpan.FromSeconds(30)).ConfigureAwait(false)); } finally { if (!NativeMethods.SetConsoleCtrlHandler(handler, add: false)) { Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); } } } } finally { SignalFromCurrentProcessLock.Release(); } }
private static bool Handler(NativeMethods.CtrlType sig) { Console.WriteLine("Stopping due to external CTRL-C, or process kill, or shutdown"); if (_ctsSource != null) { _ctsSource.Cancel(); } Thread.Sleep(500); //shutdown right away so there are no lingering threads Environment.Exit(0); return(true); }
// implementation based on https://stackoverflow.com/questions/813086/can-i-send-a-ctrl-c-sigint-to-an-application-on-windows // + some additional research / experimentation public static int Signal(int processId, NativeMethods.CtrlType ctrlType) { // first detach from the current console if one exists. We don't check the exit // code since the only documented fail case is not having a console NativeMethods.FreeConsole(); // attach to the child's console return(NativeMethods.AttachConsole(checked ((uint)processId)) // disable signal handling for our program // from https://docs.microsoft.com/en-us/windows/console/setconsolectrlhandler: // "Calling SetConsoleCtrlHandler with the NULL and TRUE arguments causes the calling process to ignore CTRL+C signals" && NativeMethods.SetConsoleCtrlHandler(null, true) // send the signal && NativeMethods.GenerateConsoleCtrlEvent(ctrlType, NativeMethods.AllProcessesWithCurrentConsoleGroup) ? 0 : Marshal.GetLastWin32Error()); }
private static bool Handler(NativeMethods.CtrlType sig) { _container.Dispose(); return(false); }
public virtual void Exit(NativeMethods.CtrlType ctrlType) { }
void OnExit(NativeMethods.CtrlType ctrlType) { Running = false; Thread.Sleep(1000); Exit(ctrlType); }