Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
mastersign committed Jun 30, 2016
2 parents 00665f8 + b6a86f3 commit 04ed65d
Show file tree
Hide file tree
Showing 40 changed files with 834 additions and 270 deletions.
11 changes: 5 additions & 6 deletions BenchManager/BenchDashboard/BenchDashboard.csproj
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\Microsoft.Net.Compilers.1.2.2\build\Microsoft.Net.Compilers.props" Condition="Exists('..\packages\Microsoft.Net.Compilers.1.2.2\build\Microsoft.Net.Compilers.props')" />
<Import Project="..\packages\Microsoft.Net.Compilers.1.3.0\build\Microsoft.Net.Compilers.props" Condition="Exists('..\packages\Microsoft.Net.Compilers.1.3.0\build\Microsoft.Net.Compilers.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
Expand Down Expand Up @@ -46,9 +46,8 @@
</PropertyGroup>
<ItemGroup>
<Reference Include="ConEmu.WinForms, Version=1.0.0.0, Culture=neutral, PublicKeyToken=00340228797aafb8, processorArchitecture=MSIL">
<HintPath>..\packages\ConEmu.Control.WinForms.1.0.20160407.0\lib\net40\ConEmu.WinForms.dll</HintPath>
<HintPath>..\packages\ConEmu.Control.WinForms.1.0.20160518.0\lib\net40\ConEmu.WinForms.dll</HintPath>
<Private>True</Private>
<Aliases>v40async</Aliases>
</Reference>
<Reference Include="System" />
<Reference Include="System.Configuration" />
Expand Down Expand Up @@ -254,14 +253,14 @@
<None Include="Resources\setup_16.png" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\packages\ConEmu.Core.16.4.16.0\build\ConEmu.Core.Targets" Condition="Exists('..\packages\ConEmu.Core.16.4.16.0\build\ConEmu.Core.Targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>Dieses Projekt verweist auf mindestens ein NuGet-Paket, das auf diesem Computer fehlt. Verwenden Sie die Wiederherstellung von NuGet-Paketen, um die fehlenden Dateien herunterzuladen. Weitere Informationen finden Sie unter "http://go.microsoft.com/fwlink/?LinkID=322105". Die fehlende Datei ist "{0}".</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\ConEmu.Core.16.4.16.0\build\ConEmu.Core.Targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\ConEmu.Core.16.4.16.0\build\ConEmu.Core.Targets'))" />
<Error Condition="!Exists('..\packages\Microsoft.Net.Compilers.1.2.2\build\Microsoft.Net.Compilers.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Net.Compilers.1.2.2\build\Microsoft.Net.Compilers.props'))" />
<Error Condition="!Exists('..\packages\Microsoft.Net.Compilers.1.3.0\build\Microsoft.Net.Compilers.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Net.Compilers.1.3.0\build\Microsoft.Net.Compilers.props'))" />
<Error Condition="!Exists('..\packages\ConEmu.Core.16.6.19.0\build\ConEmu.Core.Targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\ConEmu.Core.16.6.19.0\build\ConEmu.Core.Targets'))" />
</Target>
<Import Project="..\packages\ConEmu.Core.16.6.19.0\build\ConEmu.Core.Targets" Condition="Exists('..\packages\ConEmu.Core.16.6.19.0\build\ConEmu.Core.Targets')" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
Expand Down
275 changes: 195 additions & 80 deletions BenchManager/BenchDashboard/ConEmuExecutionHost.cs
Original file line number Diff line number Diff line change
@@ -1,47 +1,55 @@
extern alias v40async;
using v40async::ConEmu.WinForms;

using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml;
using System.IO.Pipes;
using System.Diagnostics;
using System.Windows.Forms;
using ConEmu.WinForms;

namespace Mastersign.Bench.Dashboard
{
class ConEmuExecutionHost : IProcessExecutionHost
{
private const string PowerShellHostScript = "PsExecHost.ps1";

private const int CONNECTION_TIMEOUT = 5000;

private const string EXITCODE_LINE_FORMAT = "EXITCODE {0} ";
private const string TRANSCRIPTPATH_LINE_FORMAT = "TRANSCRIPT {0} ";

private readonly ConEmuControl control;

private readonly string conEmuExe;
private readonly Core core;

private XmlDocument config;
private readonly string conEmuExe;

private volatile int runningProcesses = 0;
private readonly Semaphore hostSemaphore = new Semaphore(1, 1);

public event EventHandler ProcessIsRunningChanged;
private XmlDocument config;

public bool ProcessIsRunning { get { return runningProcesses > 0; } }
private string currentToken;
private ConEmuSession currentSession;

private IProcessExecutionHost backupHost;

public ConEmuExecutionHost(ConEmuControl control, string conEmuExe)
public ConEmuExecutionHost(Core core, ConEmuControl control, string conEmuExe)
{
this.core = core;
this.control = control;
this.conEmuExe = conEmuExe;
backupHost = new DefaultExecutionHost();
config = LoadConfigFromResource();
}

private void OnProcessIsRunningChanged()
{
var handler = ProcessIsRunningChanged;
if (handler != null) handler(this, EventArgs.Empty);
StartPowerShellExecutionHost();
this.core.ConfigReloaded += (s, e) =>
{
StopPowerShellExecutionHost();
StartPowerShellExecutionHost();
};
}

private XmlDocument LoadConfigFromResource()
Expand All @@ -56,9 +64,9 @@ private XmlDocument LoadConfigFromResource()
return doc;
}

private bool IsConEmuInstalled { get { return File.Exists(conEmuExe); } }
private bool IsConEmuInstalled => File.Exists(conEmuExe);

private ConEmuStartInfo BuildStartInfo(BenchEnvironment env, string cwd, string executable, string arguments, bool collectOutput)
private ConEmuStartInfo BuildStartInfo(string cwd, string executable, string arguments)
{
// http://www.windowsinspired.com/understanding-the-command-line-string-and-arguments-received-by-a-windows-program/

Expand All @@ -69,24 +77,11 @@ private ConEmuStartInfo BuildStartInfo(BenchEnvironment env, string cwd, string
si.ConsoleProcessCommandLine = cmdLine;
si.BaseConfiguration = config;
si.StartupDirectory = cwd;
si.IsReadingAnsiStream = collectOutput;
env.Load((k, v) => si.SetEnv(k, v));
si.IsReadingAnsiStream = false;
si.WhenConsoleProcessExits = WhenConsoleProcessExits.CloseConsoleEmulator;
return si;
}

private void NotifyProcessStart()
{
runningProcesses++;
if (runningProcesses == 1) OnProcessIsRunningChanged();
}

private void NotifyProcessEnd()
{
runningProcesses--;
if (runningProcesses == 0) OnProcessIsRunningChanged();
}

private ConEmuSession StartProcess(ConEmuStartInfo startInfo)
{
if (control.InvokeRequired)
Expand All @@ -98,84 +93,204 @@ private ConEmuSession StartProcess(ConEmuStartInfo startInfo)
return control.Start(startInfo);
}

public ProcessExecutionResult RunProcess(BenchEnvironment env,
string cwd, string executable, string arguments,
ProcessMonitoring monitoring)
private void StartPowerShellExecutionHost()
{
if (!IsConEmuInstalled)
{
return backupHost.RunProcess(env, cwd, executable, arguments, monitoring);
return;
}
var config = core.Config;
var hostScript = Path.Combine(config.GetStringValue(PropertyKeys.BenchScripts), PowerShellHostScript);
if (!File.Exists(hostScript))
{
throw new FileNotFoundException("The PowerShell host script was not found.");
}
if (!File.Exists(executable))
currentToken = Guid.NewGuid().ToString("D");
var cwd = config.GetStringValue(PropertyKeys.BenchRoot);
var startInfo = BuildStartInfo(cwd, PowerShell.Executable,
"\"" + string.Join(" ", "-NoProfile", "-NoLogo",
"-File", "\"" + hostScript + "\"",
"-Token", currentToken));
currentSession = StartProcess(startInfo);
currentSession.ConsoleEmulatorClosed += (s, o) =>
{
throw new FileNotFoundException("The executable could not be found.", executable);
currentToken = null;
currentSession = null;
};
}

private bool IsPowerShellExecutionHostRunning =>
currentSession != null;

private void WaitForSessionToEnd()
{
while (currentSession != null)
{
if (control.InvokeRequired)
{
Thread.Sleep(100);
}
else
{
Application.DoEvents();
}
}
if (!Directory.Exists(cwd))
}

private IEnumerable<string> SendCommand(string command, params string[] arguments)
{
if (!IsPowerShellExecutionHostRunning) throw new InvalidOperationException();
hostSemaphore.WaitOne();
try
{
throw new DirectoryNotFoundException("The working directory could not be found: " + cwd);
var client = new NamedPipeClientStream(".", currentToken, PipeDirection.InOut);
TextWriter w;
TextReader r;
try
{
client.Connect(CONNECTION_TIMEOUT);
w = new StreamWriter(client, Encoding.UTF8, 4, true);
r = new StreamReader(client, Encoding.UTF8, false, 4, true);
}
catch (TimeoutException)
{
yield break;
}
catch (IOException ioEx)
{
Debug.WriteLine(ioEx);
yield break;
}
w.WriteLine(command);
foreach (var arg in arguments)
{
w.WriteLine(arg);
}
w.Flush();
while (client.IsConnected)
{
var l = r.ReadLine();
if (l != null)
{
yield return l;
}
}
r.Dispose();
client.Dispose();
}
var collectOutput = (monitoring & ProcessMonitoring.Output) == ProcessMonitoring.Output;
var startInfo = BuildStartInfo(env, cwd, executable, arguments, collectOutput);
NotifyProcessStart();
var session = StartProcess(startInfo);
StringBuilder sb = collectOutput ? new StringBuilder() : null;
if (collectOutput)
finally
{
session.AnsiStreamChunkReceived += (s, e) => sb.Append(e.GetMbcsText());
hostSemaphore.Release();
}
var t = session.WaitForConsoleEmulatorCloseAsync();
t.Wait();
NotifyProcessEnd();
var exitCode = t.IsFaulted ? 999999 : session.GetConsoleProcessExitCode();
if (collectOutput)
}

private void StopPowerShellExecutionHost()
{
if (!IsPowerShellExecutionHostRunning) return;
SendCommand("close").Any(l => l == "OK");
WaitForSessionToEnd();
}

private bool ParseExitCode(string line, ref int exitCode)
{
var exitCodePrefix = string.Format(EXITCODE_LINE_FORMAT, currentToken);
if (line.StartsWith(exitCodePrefix))
{
return new ProcessExecutionResult(exitCode, sb.ToString());
var number = line.Substring(exitCodePrefix.Length);
int tmp;
if (int.TryParse(number, out tmp)) exitCode = tmp;
return true;
}
else
return false;
}

private bool ParseTranscriptPath(string line, ref string transcriptPath)
{
var exitCodePrefix = string.Format(TRANSCRIPTPATH_LINE_FORMAT, currentToken);
if (line.StartsWith(exitCodePrefix))
{
return new ProcessExecutionResult(exitCode);
transcriptPath = line.Substring(exitCodePrefix.Length);
return true;
}
return false;
}

public void StartProcess(BenchEnvironment env,
public ProcessExecutionResult RunProcess(BenchEnvironment env,
string cwd, string executable, string arguments,
ProcessExitCallback cb, ProcessMonitoring monitoring)
ProcessMonitoring monitoring)
{
if (!IsConEmuInstalled)
if (IsDisposed)
{
backupHost.StartProcess(env, cwd, executable, arguments, cb, monitoring);
return;
throw new ObjectDisposedException(nameof(ConEmuExecutionHost));
}
if (!IsPowerShellExecutionHostRunning)
{
return backupHost.RunProcess(env, cwd, executable, arguments, monitoring);
}
if (!File.Exists(executable))
var collectOutput = (monitoring & ProcessMonitoring.Output) == ProcessMonitoring.Output;
var response = SendCommand("exec", cwd, executable, arguments);
var exitCode = 999999;
var transcriptPath = default(string);
foreach (var l in response)
{
throw new FileNotFoundException("The executable could not be found.", executable);
ParseExitCode(l, ref exitCode);
ParseTranscriptPath(l, ref transcriptPath);
}
if (!Directory.Exists(cwd))
var output = default(string);
if (collectOutput && transcriptPath != null && File.Exists(transcriptPath))
{
throw new DirectoryNotFoundException("The working directory could not be found: " + cwd);
output = File.ReadAllText(transcriptPath, Encoding.Default);
File.Delete(transcriptPath);
}
var collectOutput = (monitoring & ProcessMonitoring.Output) == ProcessMonitoring.Output;
var startInfo = BuildStartInfo(env, cwd, executable, arguments, collectOutput);
NotifyProcessStart();
var session = StartProcess(startInfo);
var sb = new StringBuilder();
if (collectOutput)
return new ProcessExecutionResult(exitCode, output);
}

public void StartProcess(BenchEnvironment env,
string cwd, string executable, string arguments,
ProcessExitCallback cb, ProcessMonitoring monitoring)
{
if (IsDisposed)
{
session.AnsiStreamChunkReceived += (s, e) => sb.Append(e.GetMbcsText());
throw new ObjectDisposedException(nameof(ConEmuExecutionHost));
}
var t = session.WaitForConsoleEmulatorCloseAsync();
if (!IsPowerShellExecutionHostRunning)
{
backupHost.StartProcess(env, cwd, executable, arguments, cb, monitoring);
return;
}
var collectOutput = (monitoring & ProcessMonitoring.Output) == ProcessMonitoring.Output;
var response = SendCommand("exec", cwd, executable, arguments);
AsyncManager.StartTask(() =>
{
t.Wait();
NotifyProcessEnd();
var exitCode = t.IsFaulted ? 999999 : session.GetConsoleProcessExitCode();
var result = collectOutput
? new ProcessExecutionResult(exitCode, sb.ToString())
: new ProcessExecutionResult(exitCode);
var exitCode = 999999;
var transcriptPath = default(string);
foreach (var l in response)
{
ParseExitCode(l, ref exitCode);
ParseTranscriptPath(l, ref transcriptPath);
}
var output = default(string);
if (collectOutput && transcriptPath != null && File.Exists(transcriptPath))
{
output = File.ReadAllText(transcriptPath, Encoding.Default);
File.Delete(transcriptPath);
}
var result = new ProcessExecutionResult(exitCode, output);
cb(result);
});
}

public bool IsDisposed { get; private set; }

public void Dispose()
{
if (IsDisposed) return;
IsDisposed = true;
StopPowerShellExecutionHost();
backupHost.Dispose();
backupHost = null;
}

private delegate ConEmuSession ConEmuStarter(ConEmuStartInfo si);
}
}
Loading

0 comments on commit 04ed65d

Please sign in to comment.