Skip to content

Instantly share code, notes, and snippets.

@rwindegger
Created September 30, 2016 22:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rwindegger/f07c38fb4e0d1827e66f454b9098ca13 to your computer and use it in GitHub Desktop.
Save rwindegger/f07c38fb4e0d1827e66f454b9098ca13 to your computer and use it in GitHub Desktop.
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