/// <summary> /// End the task gracefully if possible, forcefully if necessesary. /// Disposes both the pipe and the process when done. /// Wait up to timeoutMs milliseconds for the application to close itself. /// This is optionally a blocking call, but can also be a "fire and forget" /// function by setting block=false. /// </summary> void TerminateProcess(ref WtsProcess refProcess, ref ProcessManager refManager, bool block) { var process = refProcess; var manager = refManager; refProcess = null; refManager = null; // Quick exit if process is not running if (process == null || !process.IsRunning) { if (process != null) { process.Dispose(); } if (manager != null) { manager.Close(); } return; } // Force kill the process if the pipe is not connected if (manager == null || !manager.IsConnected) { // NOTE: Pipe was connected by WaitForPipeCommand Log.Write("FORCE KILL process because pipe is not connected: Pipe=" + manager.PipeName); process.Terminate(0); process.Dispose(); if (manager != null) { manager.Close(); } return; } // Send the process a request to close in background thread Log.Write("Closing process: Pipe=" + manager.PipeName); Task.Run(async() => { try { await manager.SendCommandAsync(ProcessManager.COMMAND_CLOSE); } catch (Exception ex) { Log.Write("Exception sending close command", ex); } }); // Wait for it to exit gracefully, but kill it if still running after the timeout var task = Task.Run(async() => { try { // Give it up to one second to kill itslef var now = DateTime.Now; while ((DateTime.Now - now).TotalMilliseconds < PROCESS_TERMINATE_TIMEOUT_MS && process.IsRunning) { await Task.Delay(10); } if (process.IsRunning) { Log.Write("FORCE KILL process after sending cose command: Pipe=" + manager.PipeName); process.Terminate(0); } else { Log.Write("Process closed gracefully: Pipe=" + manager.PipeName); } process.Dispose(); manager.Close(); } catch (Exception ex) { Log.Write("Cant kill process: Pipe=" + manager.PipeName, ex); } }); if (block) { task.Wait(); } }