// Spludlow Software // Copyright © Samuel P. Ludlow 2020 All Rights Reserved // Distributed under the terms of the GNU General Public License version 3 // Distributed WITHOUT ANY WARRANTY; without implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE // https://www.spludlow.co.uk/LICENCE.TXT // The Spludlow logo is a registered trademark of Samuel P. Ludlow and may not be used without permission // v1.14 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Threading; using System.Diagnostics; namespace Spludlow { public class ProcessExecutor { private ProcessStartInfo StartInfo; private Process Process; private ManualResetEvent ManualResetEvent; private Spludlow.LogFile LogFile; private bool RestartOnExit; private string StopInput; private string OutputFilename; private class State { public bool AskedStop; } private State StateObj; public ProcessExecutor() { } public void Run( string filename, string arguments, string directory, string[] varibles, string outputFilename, bool restartOnExit, string stopInput) { this.StateObj = new State(); this.StateObj.AskedStop = false; this.RestartOnExit = restartOnExit; this.StopInput = stopInput; this.OutputFilename = outputFilename; this.StartInfo = new ProcessStartInfo(); this.StartInfo.FileName = filename; this.StartInfo.Arguments = arguments; this.StartInfo.WorkingDirectory = directory; if (varibles != null) { foreach (string varible in varibles) { string[] words = Spludlow.Text.Split(varible, '='); if (words.Length != 2) throw new ApplicationException("Environment Variables bad format: key=value,\t" + varible); this.StartInfo.EnvironmentVariables.Add(words[0], words[1]); } } this.StartInfo.CreateNoWindow = true; this.StartInfo.UseShellExecute = false; this.StartInfo.LoadUserProfile = false; this.StartInfo.RedirectStandardOutput = true; this.StartInfo.RedirectStandardError = true; this.StartInfo.RedirectStandardInput = true; this.RunWorker(); } private void RunWorker() { this.ManualResetEvent = new ManualResetEvent(false); if (this.OutputFilename != null) this.LogFile = new LogFile(this.OutputFilename); try { this.Start(); try { this.ManualResetEvent.WaitOne(); if (this.Process != null && this.Process.ExitCode != 0) throw new ApplicationException("ProcessExecutor, Exit Code:\t" + this.Process.ExitCode.ToString("X")); else Spludlow.Log.Warning("ProcessExecutor, Process Exited."); } catch (ThreadAbortException) { Spludlow.Log.Warning("ProcessExecutor, ThreadAbortException."); this.Stop(); } } finally { if (this.LogFile != null) this.LogFile.Close(); } } private void Start() { if (this.Process != null) { if (this.Process.HasExited == false) { Spludlow.Log.Warning("ProcessExecutor, Start, Killing previous process."); this.Process.Kill(); } this.Process.Dispose(); this.Process = null; } Spludlow.Log.Info("ProcessExecutor, Process Starting."); this.Process = new Process(); this.Process.StartInfo = this.StartInfo; if (this.LogFile != null) { this.Process.ErrorDataReceived += Process_ErrorDataReceived; this.Process.OutputDataReceived += Process_OutputDataReceived; } this.Process.Exited += Process_Exited; this.Process.EnableRaisingEvents = true; this.Process.Start(); this.Process.BeginOutputReadLine(); this.Process.BeginErrorReadLine(); Spludlow.Log.Info("ProcessExecutor, Process Started."); } public void Stop() { Spludlow.Log.Info("ProcessExecutor, Stopping."); try { lock (this.StateObj) this.StateObj.AskedStop = true; if (this.StopInput != null) this.Write(this.StopInput); this.Process.StandardInput.Close(); Spludlow.Log.Info("ProcessExecutor, WaitForExit."); this.Process.WaitForExit(); Spludlow.Log.Info("ProcessExecutor, Stopped."); } catch (Exception ee) { Spludlow.Log.Error("ProcessExecutor, Stopping.", ee); } } public void Write(string line) { this.Process.StandardInput.WriteLine(line); } private void Process_Exited(object sender, EventArgs e) { Spludlow.Log.Info("ProcessExecutor, Process_Exited:\t" + this.Process.ExitCode.ToString("X")); if (this.Process.ExitCode != 0) { this.ManualResetEvent.Set(); return; } lock (this.StateObj) { if (this.StateObj.AskedStop == true || this.RestartOnExit == false) { this.ManualResetEvent.Set(); return; } this.StateObj.AskedStop = false; } this.Start(); } private void Process_OutputDataReceived(object sender, DataReceivedEventArgs e) { this.ProcessOutput("OUT", e); } private void Process_ErrorDataReceived(object sender, DataReceivedEventArgs e) { this.ProcessOutput("ERR", e); } private void ProcessOutput(string subject, DataReceivedEventArgs e) { if (!String.IsNullOrEmpty(e.Data)) this.LogFile.Append(subject, e.Data); } } }