Browse Source

r7436@daniel-notebook (orig r3398): daniel | 2008-08-17 15:41:21 +0200

Fixed SD2-832: Running an external tool should use the ProcessRunner class.
 Made ProcessRunner use BeginOutputReadLine instead of running two threads for output reading.


git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/branches/3.0@3408 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Daniel Grunwald 17 years ago
parent
commit
c0a8b482e8
  1. 1
      src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj
  2. 69
      src/Main/Base/Project/Src/Commands/MenuItemBuilders.cs
  3. 98
      src/Main/Base/Project/Src/Util/OutputReader.cs
  4. 95
      src/Main/Base/Project/Src/Util/ProcessRunner.cs
  5. 2
      src/Main/StartUp/Project/Dialogs/SplashScreen.cs

1
src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj

@ -650,7 +650,6 @@ @@ -650,7 +650,6 @@
<Compile Include="Src\Util\NativeMethods.cs" />
<Compile Include="Src\Util\ProcessRunnerException.cs" />
<Compile Include="Src\Util\LineReceivedEventArgs.cs" />
<Compile Include="Src\Util\OutputReader.cs" />
<Compile Include="Src\Util\ProcessRunner.cs" />
<Compile Include="..\..\GlobalAssemblyInfo.cs">
<Link>Configuration\GlobalAssemblyInfo.cs</Link>

69
src/Main/Base/Project/Src/Commands/MenuItemBuilders.cs

@ -5,11 +5,11 @@ @@ -5,11 +5,11 @@
// <version>$Revision$</version>
// </file>
using ICSharpCode.SharpDevelop.Util;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows.Forms;
using ICSharpCode.Core;
using ICSharpCode.Core.WinForms;
using ICSharpCode.SharpDevelop.Gui;
@ -219,15 +219,6 @@ namespace ICSharpCode.SharpDevelop.Commands @@ -219,15 +219,6 @@ namespace ICSharpCode.SharpDevelop.Commands
return items;
}
void ProcessExitEvent(object sender, EventArgs e)
{
Process p = (Process)sender;
string output = p.StandardOutput.ReadToEnd();
TaskService.BuildMessageViewCategory.AppendText(output + Environment.NewLine + "${res:XML.MainMenu.ToolMenu.ExternalTools.ExitedWithCode} " + p.ExitCode + Environment.NewLine);
}
/// <summary>
/// This handler gets called when a tool in the Tool menu is clicked on.
/// </summary>
@ -255,39 +246,59 @@ namespace ICSharpCode.SharpDevelop.Commands @@ -255,39 +246,59 @@ namespace ICSharpCode.SharpDevelop.Commands
MessageService.ShowError("${res:XML.MainMenu.ToolMenu.ExternalTools.ExecutionFailed} '" + ex.Message);
return;
}
if (tool.PromptForArguments) {
args = MessageService.ShowInputBox(tool.MenuCommand, "${res:XML.MainMenu.ToolMenu.ExternalTools.EnterArguments}", args);
if (args == null)
return;
}
try {
ProcessStartInfo startinfo;
if (args == null || args.Length == 0 || args.Trim('"', ' ').Length == 0) {
startinfo = new ProcessStartInfo(command);
} else {
startinfo = new ProcessStartInfo(command, args);
}
startinfo.WorkingDirectory = StringParser.Parse(tool.InitialDirectory);
if (tool.UseOutputPad) {
startinfo.UseShellExecute = false;
startinfo.RedirectStandardOutput = true;
}
Process process = new Process();
process.EnableRaisingEvents = true;
process.StartInfo = startinfo;
if (tool.UseOutputPad) {
process.Exited += new EventHandler(ProcessExitEvent);
ProcessRunner processRunner = new ProcessRunner();
processRunner.ProcessExited += ProcessExitEvent;
processRunner.OutputLineReceived += process_OutputLineReceived;
processRunner.ErrorLineReceived += process_OutputLineReceived;
processRunner.WorkingDirectory = StringParser.Parse(tool.InitialDirectory);
if (args == null || args.Length == 0 || args.Trim('"', ' ').Length == 0) {
processRunner.Start(command);
} else {
processRunner.Start(command, args);
}
} else {
ProcessStartInfo startinfo;
if (args == null || args.Length == 0 || args.Trim('"', ' ').Length == 0) {
startinfo = new ProcessStartInfo(command);
} else {
startinfo = new ProcessStartInfo(command, args);
}
startinfo.WorkingDirectory = StringParser.Parse(tool.InitialDirectory);
Process process = new Process();
process.StartInfo = startinfo;
process.Start();
}
process.Start();
} catch (Exception ex) {
MessageService.ShowError("${res:XML.MainMenu.ToolMenu.ExternalTools.ExecutionFailed} '" + command + " " + args + "'\n" + ex.Message);
}
return;
}
}
void ProcessExitEvent(object sender, EventArgs e)
{
WorkbenchSingleton.SafeThreadAsyncCall(
delegate {
ProcessRunner p = (ProcessRunner)sender;
p.WaitForExit();
TaskService.BuildMessageViewCategory.AppendLine("${res:XML.MainMenu.ToolMenu.ExternalTools.ExitedWithCode} " + p.ExitCode);
p.Dispose();
});
}
void process_OutputLineReceived(object sender, LineReceivedEventArgs e)
{
TaskService.BuildMessageViewCategory.AppendLine(e.Line);
}
}
public class OpenContentsMenuBuilder : ISubmenuBuilder

98
src/Main/Base/Project/Src/Util/OutputReader.cs

@ -1,98 +0,0 @@ @@ -1,98 +0,0 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Matthew Ward" email="mrward@users.sourceforge.net"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading;
namespace ICSharpCode.SharpDevelop.Util
{
/// <summary>
/// A threaded <see cref="Process.StandardOutput"/> or
/// <see cref="Process.StandardError"/> reader.
/// </summary>
public class OutputReader
{
StreamReader reader;
string output = String.Empty;
Thread thread;
public event LineReceivedEventHandler LineReceived;
public OutputReader(StreamReader reader)
{
this.reader = reader;
}
/// <summary>
/// Starts reading the output stream.
/// </summary>
public void Start()
{
thread = new Thread(new ThreadStart(ReadOutput));
thread.Name = "OutputReader";
thread.Start();
}
/// <summary>
/// Gets the text output read from the reader.
/// </summary>
public string Output {
get {
return output;
}
}
/// <summary>
/// Waits for the reader to finish.
/// </summary>
public void WaitForFinish()
{
if (thread != null) {
thread.Join();
}
}
/// <summary>
/// Raises the <see cref="LineReceived"/> event.
/// </summary>
/// <param name="line"></param>
protected void OnLineReceived(string line)
{
if (LineReceived != null) {
LineReceived(this, new LineReceivedEventArgs(line));
}
}
/// <summary>
/// Reads the output stream on a different thread.
/// </summary>
void ReadOutput()
{
output = String.Empty;
StringBuilder outputBuilder = new StringBuilder();
bool endOfStream = false;
while(!endOfStream)
{
string line = reader.ReadLine();
if (line != null) {
outputBuilder.Append(line);
outputBuilder.Append(Environment.NewLine);
OnLineReceived(line);
} else {
endOfStream = true;
}
}
output = outputBuilder.ToString();
}
}
}

95
src/Main/Base/Project/Src/Util/ProcessRunner.cs

@ -8,8 +8,10 @@ @@ -8,8 +8,10 @@
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using ICSharpCode.Core;
using System.Threading;
namespace ICSharpCode.SharpDevelop.Util
{
@ -19,13 +21,12 @@ namespace ICSharpCode.SharpDevelop.Util @@ -19,13 +21,12 @@ namespace ICSharpCode.SharpDevelop.Util
/// </summary>
public class ProcessRunner : IDisposable
{
readonly object lockObj = new object();
Process process;
string standardOutput = String.Empty;
string workingDirectory = String.Empty;
OutputReader standardOutputReader;
OutputReader standardErrorReader;
StringBuilder standardOutput = new StringBuilder();
StringBuilder standardError = new StringBuilder();
ManualResetEvent endOfOutput = new ManualResetEvent(false);
int outputStreamsFinished;
/// <summary>
/// Triggered when the process has exited.
/// </summary>
@ -51,26 +52,15 @@ namespace ICSharpCode.SharpDevelop.Util @@ -51,26 +52,15 @@ namespace ICSharpCode.SharpDevelop.Util
/// <summary>
/// Gets or sets the process's working directory.
/// </summary>
public string WorkingDirectory {
get {
return workingDirectory;
}
set {
workingDirectory = value;
}
}
public string WorkingDirectory { get; set; }
/// <summary>
/// Gets the standard output returned from the process.
/// </summary>
public string StandardOutput {
get {
string output = String.Empty;
if (standardOutputReader != null) {
output = standardOutputReader.Output;
}
return output;
lock (standardOutput)
return standardOutput.ToString();
}
}
@ -79,11 +69,8 @@ namespace ICSharpCode.SharpDevelop.Util @@ -79,11 +69,8 @@ namespace ICSharpCode.SharpDevelop.Util
/// </summary>
public string StandardError {
get {
string output = String.Empty;
if (standardErrorReader != null) {
output = standardErrorReader.Output;
}
return output;
lock (standardError)
return standardError.ToString();
}
}
@ -92,6 +79,8 @@ namespace ICSharpCode.SharpDevelop.Util @@ -92,6 +79,8 @@ namespace ICSharpCode.SharpDevelop.Util
/// </summary>
public void Dispose()
{
process.Dispose();
endOfOutput.Close();
}
/// <summary>
@ -130,8 +119,7 @@ namespace ICSharpCode.SharpDevelop.Util @@ -130,8 +119,7 @@ namespace ICSharpCode.SharpDevelop.Util
bool exited = process.WaitForExit(timeout);
if (exited) {
standardOutputReader.WaitForFinish();
standardErrorReader.WaitForFinish();
endOfOutput.WaitOne(timeout);
}
return exited;
@ -160,9 +148,11 @@ namespace ICSharpCode.SharpDevelop.Util @@ -160,9 +148,11 @@ namespace ICSharpCode.SharpDevelop.Util
process = new Process();
process.StartInfo.CreateNoWindow = true;
process.StartInfo.FileName = command;
process.StartInfo.WorkingDirectory = workingDirectory;
process.StartInfo.WorkingDirectory = WorkingDirectory;
process.StartInfo.RedirectStandardOutput = true;
process.OutputDataReceived += OnOutputLineReceived;
process.StartInfo.RedirectStandardError = true;
process.ErrorDataReceived += OnErrorLineReceived;
process.StartInfo.UseShellExecute = false;
process.StartInfo.Arguments = arguments;
@ -171,7 +161,6 @@ namespace ICSharpCode.SharpDevelop.Util @@ -171,7 +161,6 @@ namespace ICSharpCode.SharpDevelop.Util
process.Exited += OnProcessExited;
}
lock (lockObj) {
bool started = false;
try {
process.Start();
@ -183,20 +172,8 @@ namespace ICSharpCode.SharpDevelop.Util @@ -183,20 +172,8 @@ namespace ICSharpCode.SharpDevelop.Util
}
}
standardOutputReader = new OutputReader(process.StandardOutput);
if (OutputLineReceived != null) {
standardOutputReader.LineReceived += new LineReceivedEventHandler(OnOutputLineReceived);
}
standardOutputReader.Start();
standardErrorReader = new OutputReader(process.StandardError);
if (ErrorLineReceived != null) {
standardErrorReader.LineReceived += new LineReceivedEventHandler(OnErrorLineReceived);
}
standardErrorReader.Start();
}
process.BeginOutputReadLine();
process.BeginErrorReadLine();
}
/// <summary>
@ -219,8 +196,7 @@ namespace ICSharpCode.SharpDevelop.Util @@ -219,8 +196,7 @@ namespace ICSharpCode.SharpDevelop.Util
process.Close();
process.Dispose();
process = null;
standardOutputReader.WaitForFinish();
standardErrorReader.WaitForFinish();
endOfOutput.WaitOne();
} else {
process = null;
}
@ -233,9 +209,8 @@ namespace ICSharpCode.SharpDevelop.Util @@ -233,9 +209,8 @@ namespace ICSharpCode.SharpDevelop.Util
protected void OnProcessExited(object sender, EventArgs e)
{
if (ProcessExited != null) {
lock (lockObj) {
standardOutputReader.WaitForFinish();
standardErrorReader.WaitForFinish();
if (endOfOutput != null) {
endOfOutput.WaitOne();
}
ProcessExited(this, e);
@ -247,10 +222,18 @@ namespace ICSharpCode.SharpDevelop.Util @@ -247,10 +222,18 @@ namespace ICSharpCode.SharpDevelop.Util
/// </summary>
/// <param name="sender">The event source.</param>
/// <param name="e">The line received event arguments.</param>
protected void OnOutputLineReceived(object sender, LineReceivedEventArgs e)
protected void OnOutputLineReceived(object sender, DataReceivedEventArgs e)
{
if (e.Data == null) {
if (Interlocked.Increment(ref outputStreamsFinished) == 2)
endOfOutput.Set();
return;
}
lock (standardOutput) {
standardOutput.AppendLine(e.Data);
}
if (OutputLineReceived != null) {
OutputLineReceived(this, e);
OutputLineReceived(this, new LineReceivedEventArgs(e.Data));
}
}
@ -259,10 +242,18 @@ namespace ICSharpCode.SharpDevelop.Util @@ -259,10 +242,18 @@ namespace ICSharpCode.SharpDevelop.Util
/// </summary>
/// <param name="sender">The event source.</param>
/// <param name="e">The line received event arguments.</param>
protected void OnErrorLineReceived(object sender, LineReceivedEventArgs e)
protected void OnErrorLineReceived(object sender, DataReceivedEventArgs e)
{
if (e.Data == null) {
if (Interlocked.Increment(ref outputStreamsFinished) == 2)
endOfOutput.Set();
return;
}
lock (standardError) {
standardError.AppendLine(e.Data);
}
if (ErrorLineReceived != null) {
ErrorLineReceived(this, e);
ErrorLineReceived(this, new LineReceivedEventArgs(e.Data));
}
}
}

2
src/Main/StartUp/Project/Dialogs/SplashScreen.cs

@ -14,7 +14,7 @@ namespace ICSharpCode.SharpDevelop @@ -14,7 +14,7 @@ namespace ICSharpCode.SharpDevelop
{
public class SplashScreenForm : Form
{
public const string VersionText = "SharpDevelop 3.0.0." + RevisionClass.Revision;
public const string VersionText = "SharpDevelop " + RevisionClass.FullVersion;
static SplashScreenForm splashScreen;
static List<string> requestedFileList = new List<string>();

Loading…
Cancel
Save