Files
wg_cpso/CaeJob/ExecutableJob.cs

335 lines
11 KiB
C#
Raw Permalink Normal View History

2026-03-25 18:20:24 +08:00
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<string> 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 { }
}
}
}