using System.Diagnostics; using System.Runtime.Versioning; using System.Text; namespace Geekeey.Extensions.Process; internal partial class ProcessHandle { private const string CSharpSourceText = """ [DllImport("kernel32.dll", SetLastError = true)] public static extern bool FreeConsole(); [DllImport("kernel32.dll", SetLastError = true)] public static extern bool AttachConsole(uint p); [DllImport("kernel32.dll", SetLastError = true)] public static extern bool SetConsoleCtrlHandler(uint p, bool a); [DllImport("kernel32.dll", SetLastError = true)] public static extern bool GenerateConsoleCtrlEvent(uint e, uint p); public static int NotifyCtrlEvent(uint pid, uint sig) { // detach from the current console, if one is attached to the process. FreeConsole(); var success = // attach to the target process group AttachConsole(pid) && // set to ignore signals on self, so the proper exit code can be return SetConsoleCtrlHandler(0, true) && // send the signal to the process group GenerateConsoleCtrlEvent(sig, 0); return success ? 0 : Marshal.GetLastWin32Error(); } """; private const string PowerShellSourceText = """ Add-Type -Namespace 'Win32' -Name 'Notify' -Language CSharp -MemberDefinition '{0}'; exit [Win32.Notify]::NotifyCtrlEvent({1}, {2}) """; [SupportedOSPlatform("windows")] private static bool SendCtrlSignal(int processId, ConsoleCtrlEvent ctrl) { using var process = new System.Diagnostics.Process(); var text = string.Format(PowerShellSourceText, CSharpSourceText, processId, (uint)ctrl); text = Convert.ToBase64String(Encoding.Unicode.GetBytes(text)); process.StartInfo = new ProcessStartInfo { FileName = "powershell.exe", Arguments = $"-NoLogo -NoProfile -ExecutionPolicy Bypass -EncodedCommand {text}", CreateNoWindow = true, UseShellExecute = false }; if (!process.Start()) return false; if (!process.WaitForExit(30_000)) return false; return process.ExitCode == 0; } internal enum ConsoleCtrlEvent { CTRL_C_EVENT = 0, // SIGINT CTRL_BREAK_EVENT = 1, // SIGQUIT CTRL_CLOSE_EVENT = 2, // SIGHUP CTRL_LOGOFF_EVENT = 5, // SIGHUP CTRL_SHUTDOWN_EVENT = 6, // SIGTERM } }