Implements the base process class
/// @license <![CDATA[Copyright © windegger.wtf 2016 | |
/// | |
/// Unauthorized copying of this file, via any medium is strictly | |
/// prohibited | |
/// | |
/// Proprietary and confidential | |
/// | |
/// Written by Rene Windegger <rene@windegger.wtf> on 24.02.2016]]> | |
/// @file Diagnostics\BaseProcess.cs | |
/// Implements the base process class. | |
namespace wtf.windegger.SharedLibrary.Diagnostics | |
{ | |
using System; | |
using System.Collections.Generic; | |
using System.Text; | |
using System.Threading.Tasks; | |
using System.Threading; | |
using Threading; | |
using System.Diagnostics; | |
/// See . | |
/// @author Rene Windegger | |
/// @date 24.02.2016 | |
/// @sa System.Diagnostics.Process | |
public abstract class BaseProcess<T, TEnvironment> : Process | |
where T : BaseProcess<T, TEnvironment>, new() | |
where TEnvironment : IEnvironmentBuilder | |
{ | |
#region Static Properties | |
/// Gets the process scheduler. | |
/// @return The process scheduler. | |
public static LimitedConcurrencyTaskScheduler ProcessScheduler { get; } = new LimitedConcurrencyTaskScheduler(1); | |
/// Gets the processes. | |
/// @return The processes. | |
public static LinkedList Processes { get; } = new LinkedList(); | |
#endregion | |
#region Static Methods | |
/// Starts the given environment. | |
/// @author Rene Windegger | |
/// @date 24.02.2016 | |
/// @param environment The environment. | |
/// @return A Task | |
public static Task Start(TEnvironment environment) | |
{ | |
return Task.Factory.StartNew(() => new T()) | |
.ContinueWith(p => { Processes.AddLast(p.Result); return p.Result; }) | |
.ContinueWith(p => { p.Result.Execute(environment); return p.Result; }, ProcessScheduler) | |
.ContinueWith(p => { Processes.Remove(p.Result); return p.Result; }); | |
} | |
/// Starts. | |
/// @author Rene Windegger | |
/// @date 24.02.2016 | |
/// @param environment The environment. | |
/// @param token The token. | |
/// @return A Task | |
public static Task Start(TEnvironment environment, CancellationToken token) | |
{ | |
return Task.Factory.StartNew(() => new T(), token) | |
.ContinueWith(p => { Processes.AddLast(p.Result); return p.Result; }, token) | |
.ContinueWith(p => { p.Result.Execute(environment); return p.Result; }, token, TaskContinuationOptions.LongRunning, ProcessScheduler) | |
.ContinueWith(p => { Processes.Remove(p.Result); return p.Result; }, token); | |
} | |
#endregion | |
#region Properties | |
/// Gets the command line. | |
/// @return The command line. | |
public string CommandLine { get; } | |
/// Gets the environment. | |
/// @return The environment. | |
public TEnvironment Environment { get; private set; } | |
#endregion | |
#region Fields | |
private StringBuilder m_ConsoleBuilder = new StringBuilder(); ///< The console builder | |
private readonly object m_DataReceivedLock = new object(); ///< The data received lock | |
#endregion | |
/// Initializes a new instance of the | |
/// wtf.windegger.SharedLibrary.Diagnostics.BaseProcess<T, | |
/// TEnvironment> class. | |
/// @author Rene Windegger | |
/// @date 24.02.2016 | |
/// @param commandLine The command line. | |
public BaseProcess(string commandLine) : base() | |
{ | |
CommandLine = commandLine; | |
} | |
#region Protected Methods | |
/// Executes the given environment. | |
/// @author Rene Windegger | |
/// @date 24.02.2016 | |
/// @param env The environment. | |
protected void Execute(TEnvironment env) | |
{ | |
Environment = env; | |
ProcessStartInfo info = new ProcessStartInfo(CommandLine, Environment.BuildArgumentLine()); | |
info.RedirectStandardError = true; | |
info.RedirectStandardInput = true; | |
info.RedirectStandardOutput = true; | |
info.UseShellExecute = false; | |
info.CreateNoWindow = true; | |
info.WorkingDirectory = Environment.WorkingDirectory; | |
foreach (var e in Environment.BuildEnvironmentVariables()) | |
{ | |
#if NET451 | |
info.EnvironmentVariables.Add(e.Key, e.Value); | |
#else | |
info.Environment.Add(e.Key, e.Value); | |
#endif | |
} | |
StartInfo = info; | |
ErrorDataReceived += DataReceived; | |
OutputDataReceived += DataReceived; | |
Start(); | |
Environment.IsRunning = true; | |
BeginErrorReadLine(); | |
BeginOutputReadLine(); | |
#if NET451 | |
AppDomain.CurrentDomain.ProcessExit += ApplicationExit; | |
#endif | |
WaitForExit(); | |
if (HasExited) | |
{ | |
CancelErrorRead(); | |
CancelOutputRead(); | |
#if NET451 | |
Close(); | |
AppDomain.CurrentDomain.ProcessExit -= ApplicationExit; | |
#endif | |
Environment.IsRunning = false; | |
Environment.IsFinished = true; | |
} | |
} | |
#endregion | |
#region Private Methods | |
/// Application exit. | |
/// @author Rene Windegger | |
/// @date 24.02.2016 | |
/// @param sender Source of the event. | |
/// @param e Event information. | |
private void ApplicationExit(object sender, EventArgs e) | |
{ | |
ShutdownRequested(); | |
} | |
/// Data received. | |
/// @author Rene Windegger | |
/// @date 24.02.2016 | |
/// @param sender Source of the event. | |
/// @param e Data received event information. | |
private void DataReceived(object sender, DataReceivedEventArgs e) | |
{ | |
if (!string.IsNullOrWhiteSpace(e.Data?.Trim())) | |
{ | |
string line = e.Data.Trim(); | |
lock (m_DataReceivedLock) | |
{ | |
m_ConsoleBuilder.AppendLine(line); | |
} | |
Environment.Console = m_ConsoleBuilder.ToString(); | |
LineReceived(line); | |
} | |
} | |
#endregion | |
#region Abstract Methods | |
/// Shutdown requested. | |
/// @author Rene Windegger | |
/// @date 24.02.2016 | |
protected abstract void ShutdownRequested(); | |
/// Line received. | |
/// @author Rene Windegger | |
/// @date 24.02.2016 | |
/// @param line The line. | |
protected abstract void LineReceived(string line); | |
#endregion | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment