using CaeGlobals; using System; using System.Diagnostics; using System.IO; using System.Management; using System.Text; using System.Threading; namespace CaeJob { [Serializable] public class ExecutableJob : NamedClass { // Variables // 非序列化变量 [NonSerialized] protected Stopwatch _watch; [NonSerialized] protected Stopwatch _updateWatch; // timer does not tick - use update watch [NonSerialized] private Process _exe; [NonSerialized] AutoResetEvent _outputWaitHandle; [NonSerialized] AutoResetEvent _errorWaitHandle; [NonSerialized] private StringBuilder _sbOutput; [NonSerialized] private string _outputFileName; [NonSerialized] private string _errorFileName; [NonSerialized] private object _myLock; // Properties public override string Name { get => base.Name; set { base.Name = value; Argument = Name; } } public string WorkDirectory { get; set; } public string Executable { get; set; } public string Argument { get; set; } public JobStatus JobStatus { get; protected set; } public string OutputData { get { try { if (_sbOutput != null) { if (_myLock == null) { _myLock = new object(); } lock (_myLock) { return _sbOutput.ToString(); } } return null; } catch { return null; } } } // Events public event Action AppendOutput; // Constructor public ExecutableJob(string name, string executable, string argument, string workDirectory) : base(name) { Executable = executable; Argument = argument; WorkDirectory = workDirectory; // _exe = null; JobStatus = JobStatus.None; _watch = null; _updateWatch = null; _sbOutput = null; } // Methods public void Submit() { if (_myLock == null) _myLock = new object(); lock (_myLock) { if (_sbOutput == null) _sbOutput = new StringBuilder(); _sbOutput.Clear(); } // AddDataToOutput("Running command: " + Executable + " " + Argument); // _watch = new Stopwatch(); _updateWatch = new Stopwatch(); // JobStatus = JobStatus.Running; // _watch.Start(); _updateWatch.Start(); // Run(); // RunCompleted(); } private void Run() { if (!File.Exists(Executable)) throw new Exception("The file '" + Executable + "' does not exist."); if (!Tools.WaitForFileToUnlock(Executable, 5000)) throw new Exception("The mesher is busy."); // string tmpName = Path.GetFileName(Name); _outputFileName = Path.Combine(WorkDirectory, "_output_" + tmpName + ".txt"); _errorFileName = Path.Combine(WorkDirectory, "_error_" + tmpName + ".txt"); // if (File.Exists(_outputFileName)) File.Delete(_outputFileName); if (File.Exists(_errorFileName)) File.Delete(_errorFileName); // ProcessStartInfo psi = new ProcessStartInfo(); psi.CreateNoWindow = true; psi.FileName = Executable; psi.Arguments = Argument; psi.WorkingDirectory = WorkDirectory; psi.WindowStyle = ProcessWindowStyle.Normal; psi.UseShellExecute = false; psi.RedirectStandardOutput = true; psi.RedirectStandardInput = true; // sometimes needed psi.RedirectStandardError = true; // Debug.WriteLine(DateTime.Now + " Start proces: " + tmpName + " in: " + WorkDirectory); // _exe = new Process(); _exe.StartInfo = psi; // _outputWaitHandle = new AutoResetEvent(false); _errorWaitHandle = new AutoResetEvent(false); { _exe.OutputDataReceived += _exe_OutputDataReceived; _exe.ErrorDataReceived += _exe_ErrorDataReceived; // _exe.Start(); _exe.BeginOutputReadLine(); _exe.BeginErrorReadLine(); // int ms = 1000 * 3600 * 24 * 7 * 3; // 3 weeks // if (_exe.WaitForExit(ms) && _outputWaitHandle.WaitOne(ms) && _errorWaitHandle.WaitOne(ms)) { // Process completed. Check process. ExitCode here. After Kill() _jobStatus is Killed if (JobStatus == JobStatus.Running) { JobStatus = JobStatus.OK; if (_exe.ExitCode != 0) JobStatus = JobStatus.Failed; } } else { // Timed out. Kill("Time out."); JobStatus = JobStatus.TimedOut; } _exe.Close(); } } private void RunCompatible() { ProcessStartInfo psi = new ProcessStartInfo(); psi.FileName = Executable; psi.Arguments = Argument; psi.WorkingDirectory = WorkDirectory; psi.UseShellExecute = false; // //SetEnvironmentVariables(psi); // _exe = new Process(); _exe.StartInfo = psi; _exe.Start(); // int ms = 1000 * 3600 * 24 * 7 * 3; // 3 weeks if (_exe.WaitForExit(ms)) { // Process completed. Check process.ExitCode here. // after Kill() _jobStatus is Killed JobStatus = JobStatus.OK; } else { // Timed out. Kill("Time out."); //Debug.WriteLine(DateTime.Now + " Timeout proces: " + Name + " in: " + _workDirectory); JobStatus = JobStatus.TimedOut; } _exe.Close(); } private void _exe_ErrorDataReceived(object sender, DataReceivedEventArgs e) { if (e.Data == null) { // The safe wait handle closes on kill if (!_errorWaitHandle.SafeWaitHandle.IsClosed) _errorWaitHandle.Set(); } else { File.AppendAllText(_errorFileName, e.Data + Environment.NewLine); AddDataToOutput(e.Data); } } private void _exe_OutputDataReceived(object sender, DataReceivedEventArgs e) { if (e.Data == null) { // The safe wait handle closes on kill if (!_outputWaitHandle.SafeWaitHandle.IsClosed) _outputWaitHandle.Set(); } else { AddDataToOutput(e.Data); } } void RunCompleted() { _watch.Stop(); _updateWatch.Stop(); // AddDataToOutput(""); AddDataToOutput("Elapsed time [s]: " + _watch.Elapsed.TotalSeconds.ToString()); // UpdateOutput(); // Dereference the link to otheh objects AppendOutput = null; } // public void Kill(string message) { try { if (_exe != null) { AddDataToOutput(message); // _watch.Stop(); _updateWatch.Stop(); // JobStatus = JobStatus.Killed; // this has to be here before _exe.Kill, to return the correct status // KillAllProcessesSpawnedBy((UInt32)_exe.Id); // _exe.Kill(); } } catch { } finally { // Dereference the link to other objects AppendOutput = null; } } public static void KillAllProcessesSpawnedBy(UInt32 parentProcessId) { // NOTE: Process Ids are reused! ManagementObjectSearcher searcher = new ManagementObjectSearcher( "SELECT * " + "FROM Win32_Process " + "WHERE ParentProcessId=" + parentProcessId); ManagementObjectCollection collection = searcher.Get(); if (collection.Count > 0) { foreach (var item in collection) { UInt32 childProcessId = (UInt32)item["ProcessId"]; if ((int)childProcessId != Process.GetCurrentProcess().Id) { KillAllProcessesSpawnedBy(childProcessId); try { Process childProcess = Process.GetProcessById((int)childProcessId); childProcess.Kill(); } catch { } } } } } private void AddDataToOutput(string data) { if (_myLock == null) _myLock = new object(); lock (_myLock) _sbOutput.AppendLine(data); // if (_updateWatch != null && _updateWatch.ElapsedMilliseconds > 1000) { UpdateOutput(); _updateWatch.Restart(); } } private void UpdateOutput() { try { AppendOutput?.Invoke(OutputData); // if (_myLock == null) _myLock = new object(); lock (_myLock) { //if (Tools.WaitForFileToUnlock(_outputFileName, 5000)) if (_outputFileName != null) { File.AppendAllText(_outputFileName, OutputData); _sbOutput.Clear(); } } } catch { } } } }