Last active
August 29, 2015 13:59
-
-
Save rostreim/10558496 to your computer and use it in GitHub Desktop.
A WinForm Console Shell Control
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // | |
| // Copyright 2014, Desert Software Solutions Inc. | |
| // CommandShellControl.cs: https://gist.github.com/rostreim/10558496 | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| // | |
| using System; | |
| using System.Collections.Generic; | |
| using System.ComponentModel; | |
| using System.Drawing; | |
| using System.Linq; | |
| using System.Windows.Forms; | |
| namespace DesertSoftware.ShellControl | |
| { | |
| /// <summary> | |
| /// The console event handler is used for console events. | |
| /// </summary> | |
| /// <param name="sender">The sender.</param> | |
| /// <param name="args">The <see cref="ConsoleEventArgs"/> instance containing the event data.</param> | |
| public delegate void ConsoleEventHandler(object sender, ConsoleEventArgs args); | |
| /// <summary> | |
| /// The Command Shell Control allows you to embed a basic console in your application. | |
| /// </summary> | |
| [ToolboxBitmap(typeof(Resfinder), "ConsoleControl.ConsoleControl.bmp")] | |
| public partial class CommandShellControl : UserControl | |
| { | |
| /// <summary> | |
| /// Initializes a new instance of the <see cref="CommandShellControl"/> class. | |
| /// </summary> | |
| public CommandShellControl() { | |
| // Initialise the component. | |
| InitializeComponent(); | |
| // Show diagnostics disabled by default. | |
| ShowDiagnostics = false; | |
| // Input enabled by default. | |
| AllowInput = true; | |
| // Handle process events. | |
| processManager.OnProcessOutput += processInterace_OnProcessOutput; | |
| processManager.OnProcessError += processInterace_OnProcessError; | |
| processManager.OnProcessInput += processInterace_OnProcessInput; | |
| processManager.OnProcessExit += processInterace_OnProcessExit; | |
| // Wait for key down messages on the rich text box. | |
| richTextBoxConsole.KeyDown += richTextBoxConsole_KeyDown; | |
| } | |
| /// <summary> | |
| /// Handles the OnProcessError event of the processInterace control. | |
| /// </summary> | |
| /// <param name="sender">The source of the event.</param> | |
| /// <param name="args">The <see cref="ProcessEventArgs"/> instance containing the event data.</param> | |
| void processInterace_OnProcessError(object sender, ProcessEventArgs args) { | |
| // Write the output, in red | |
| WriteOutput(args.Content, Color.Red); | |
| // Fire the output event. | |
| FireConsoleOutputEvent(args.Content); | |
| } | |
| /// <summary> | |
| /// Handles the OnProcessOutput event of the processInterace control. | |
| /// </summary> | |
| /// <param name="sender">The source of the event.</param> | |
| /// <param name="args">The <see cref="ProcessEventArgs"/> instance containing the event data.</param> | |
| void processInterace_OnProcessOutput(object sender, ProcessEventArgs args) { | |
| // Write the output, in white | |
| WriteOutput(args.Content, Color.White); | |
| // Fire the output event. | |
| FireConsoleOutputEvent(args.Content); | |
| } | |
| /// <summary> | |
| /// Handles the OnProcessInput event of the processInterace control. | |
| /// </summary> | |
| /// <param name="sender">The source of the event.</param> | |
| /// <param name="args">The <see cref="ProcessEventArgs"/> instance containing the event data.</param> | |
| void processInterace_OnProcessInput(object sender, ProcessEventArgs args) { | |
| throw new NotImplementedException(); | |
| } | |
| /// <summary> | |
| /// Handles the OnProcessExit event of the processInterace control. | |
| /// </summary> | |
| /// <param name="sender">The source of the event.</param> | |
| /// <param name="args">The <see cref="ProcessEventArgs"/> instance containing the event data.</param> | |
| void processInterace_OnProcessExit(object sender, ProcessEventArgs args) { | |
| // Are we showing diagnostics? | |
| if (ShowDiagnostics) { | |
| WriteOutput(Environment.NewLine + processManager.ProcessFileName + " exited.", Color.FromArgb(255, 0, 255, 0)); | |
| } | |
| if (!this.IsHandleCreated) | |
| return; | |
| // Read only again. | |
| Invoke((Action)(() => { | |
| richTextBoxConsole.ReadOnly = true; | |
| })); | |
| } | |
| ///// <summary> | |
| ///// Initialises the key mappings. | |
| ///// </summary> | |
| //private void InitialiseKeyMappings() | |
| //{ | |
| // // Map 'tab'. | |
| // keyMappings.Add(new KeyMapping(false, false, false, Keys.Tab, "{TAB}", "\t")); | |
| // // Map 'Ctrl-C'. | |
| // keyMappings.Add(new KeyMapping(true, false, false, Keys.C, "^(c)", "\x03\r\n")); | |
| //} | |
| /// <summary> | |
| /// Handles the KeyDown event of the richTextBoxConsole control. | |
| /// </summary> | |
| /// <param name="sender">The source of the event.</param> | |
| /// <param name="e">The <see cref="System.Windows.Forms.KeyEventArgs"/> instance containing the event data.</param> | |
| void richTextBoxConsole_KeyDown(object sender, KeyEventArgs e) { | |
| // If we're at the input point and it's backspace, bail. | |
| if ((richTextBoxConsole.SelectionStart <= inputStart) && e.KeyCode == Keys.Back) e.SuppressKeyPress = true; | |
| // Are we in the read-only zone? | |
| if (richTextBoxConsole.SelectionStart < inputStart) { | |
| // Allow arrows, control, shift | |
| if (!(e.KeyCode == Keys.Left || e.KeyCode == Keys.ShiftKey || e.KeyCode == Keys.ControlKey || | |
| e.KeyCode == Keys.Right || e.KeyCode == Keys.Up || e.KeyCode == Keys.Down)) { | |
| richTextBoxConsole.SelectionStart = richTextBoxConsole.TextLength; | |
| } | |
| } | |
| if (e.KeyCode == Keys.K && e.Control) { | |
| WriteInput("\x03\r\n", Color.WhiteSmoke, false); | |
| } | |
| // Is it the return key? | |
| if (e.KeyCode == Keys.Return) { | |
| // Get the input. | |
| string input = richTextBoxConsole.Text.Substring(inputStart, richTextBoxConsole.TextLength - inputStart); | |
| // (richTextBoxConsole.SelectionStart) - inputStart); | |
| // Write the input (without echoing). | |
| WriteInput(input, Color.White, false); | |
| } | |
| } | |
| /// <summary> | |
| /// Writes the output to the console control. | |
| /// </summary> | |
| /// <param name="output">The output.</param> | |
| /// <param name="color">The color.</param> | |
| public void WriteOutput(string output, Color color) { | |
| if (string.IsNullOrEmpty(lastInput) == false && | |
| (output == lastInput || output.Replace("\r\n", "") == lastInput)) | |
| return; | |
| if (!this.IsHandleCreated) | |
| return; | |
| Invoke((Action)(() => { | |
| // Write the output. | |
| richTextBoxConsole.SelectionColor = color; | |
| richTextBoxConsole.SelectedText += output; | |
| inputStart = richTextBoxConsole.SelectionStart; | |
| })); | |
| } | |
| /// <summary> | |
| /// Clears the output. | |
| /// </summary> | |
| public void ClearOutput() { | |
| richTextBoxConsole.Clear(); | |
| inputStart = 0; | |
| } | |
| /// <summary> | |
| /// Writes the line of input to the console control. | |
| /// </summary> | |
| /// <param name="input">The input.</param> | |
| /// <param name="color">The color.</param> | |
| /// <param name="echo">if set to <c>true</c> echo the input.</param> | |
| public void WriteInput(string input, Color color, bool echo) { | |
| Invoke((Action)(() => { | |
| // Are we echoing? | |
| if (echo) { | |
| if (richTextBoxConsole.SelectionStart < inputStart) | |
| richTextBoxConsole.SelectionStart = richTextBoxConsole.TextLength; | |
| richTextBoxConsole.SelectionColor = color; | |
| richTextBoxConsole.SelectedText += input + "\n"; | |
| inputStart = richTextBoxConsole.SelectionStart; | |
| } | |
| lastInput = input; | |
| // Write the input. | |
| processManager.WriteInput(input); | |
| // Fire the event. | |
| FireConsoleInputEvent(input); | |
| })); | |
| } | |
| /// <summary> | |
| /// Runs a process. | |
| /// </summary> | |
| /// <param name="fileName">Name of the file.</param> | |
| /// <param name="arguments">The arguments.</param> | |
| public void StartProcess(string fileName, string arguments) { | |
| // Are we showing diagnostics? | |
| if (ShowDiagnostics) { | |
| WriteOutput("Preparing to run " + fileName, Color.FromArgb(255, 0, 255, 0)); | |
| if (!string.IsNullOrEmpty(arguments)) | |
| WriteOutput(" with arguments " + arguments + "." + Environment.NewLine, Color.FromArgb(255, 0, 255, 0)); | |
| else | |
| WriteOutput("." + Environment.NewLine, Color.FromArgb(255, 0, 255, 0)); | |
| } | |
| // Start the process. | |
| processManager.StartProcess(fileName, arguments); | |
| // If we enable input, make the control not read only. | |
| if (this.allowInput) | |
| richTextBoxConsole.ReadOnly = false; | |
| } | |
| /// <summary> | |
| /// Stops the process. | |
| /// </summary> | |
| public void StopProcess() { | |
| // Stop the interface. | |
| processManager.StopProcess(); | |
| } | |
| /// <summary> | |
| /// Fires the console output event. | |
| /// </summary> | |
| /// <param name="content">The content.</param> | |
| private void FireConsoleOutputEvent(string content) { | |
| // Get the event. | |
| var theEvent = OnConsoleOutput; | |
| if (theEvent != null) | |
| theEvent(this, new ConsoleEventArgs(content)); | |
| } | |
| /// <summary> | |
| /// Fires the console input event. | |
| /// </summary> | |
| /// <param name="content">The content.</param> | |
| private void FireConsoleInputEvent(string content) { | |
| // Get the event. | |
| var theEvent = OnConsoleInput; | |
| if (theEvent != null) | |
| theEvent(this, new ConsoleEventArgs(content)); | |
| } | |
| /// <summary> | |
| /// The internal process interface used to interface with the process. | |
| /// </summary> | |
| private readonly ProcessManager processManager = new ProcessManager(); | |
| /// <summary> | |
| /// Current position that input starts at. | |
| /// </summary> | |
| int inputStart = -1; | |
| /// <summary> | |
| /// The is input enabled flag. | |
| /// </summary> | |
| private bool allowInput = true; | |
| /// <summary> | |
| /// The last input string (used so that we can make sure we don't echo input twice). | |
| /// </summary> | |
| private string lastInput; | |
| ///// <summary> | |
| ///// The key mappings. | |
| ///// </summary> | |
| //private List<KeyMapping> keyMappings = new List<KeyMapping>(); | |
| /// <summary> | |
| /// Occurs when console output is produced. | |
| /// </summary> | |
| public event ConsoleEventHandler OnConsoleOutput; | |
| /// <summary> | |
| /// Occurs when console input is produced. | |
| /// </summary> | |
| public event ConsoleEventHandler OnConsoleInput; | |
| /// <summary> | |
| /// Gets or sets a value indicating whether to show diagnostics. | |
| /// </summary> | |
| /// <value> | |
| /// <c>true</c> if show diagnostics; otherwise, <c>false</c>. | |
| /// </value> | |
| [Category("Console Control"), Description("Show diagnostic information, such as exceptions.")] | |
| public bool ShowDiagnostics { get; set; } | |
| /// <summary> | |
| /// Gets or sets a value indicating whether this instance is input enabled. | |
| /// </summary> | |
| /// <value> | |
| /// <c>true</c> if this instance is input enabled; otherwise, <c>false</c>. | |
| /// </value> | |
| [Category("Console Control"), Description("If true, the user can key in input.")] | |
| public bool AllowInput { | |
| get { return this.allowInput; } | |
| set { | |
| this.allowInput = value; | |
| if (IsProcessRunning) | |
| richTextBoxConsole.ReadOnly = !value; | |
| } | |
| } | |
| /// <summary> | |
| /// Gets a value indicating whether this instance is process running. | |
| /// </summary> | |
| /// <value> | |
| /// <c>true</c> if this instance is process running; otherwise, <c>false</c>. | |
| /// </value> | |
| [Browsable(false)] | |
| public bool IsProcessRunning { | |
| get { return processManager.IsProcessRunning; } | |
| } | |
| /// <summary> | |
| /// Gets the internal rich text box. | |
| /// </summary> | |
| [Browsable(false)] | |
| public RichTextBox InternalRichTextBox { | |
| get { return richTextBoxConsole; } | |
| } | |
| /// <summary> | |
| /// Gets the process interface. | |
| /// </summary> | |
| [Browsable(false)] | |
| public ProcessManager ProcessInterface { | |
| get { return processManager; } | |
| } | |
| ///// <summary> | |
| ///// Gets the key mappings. | |
| ///// </summary> | |
| //[Browsable(false)] | |
| //public List<KeyMapping> KeyMappings { | |
| // get { return keyMappings; } | |
| //} | |
| /// <summary> | |
| /// Gets or sets the font of the text displayed by the control. | |
| /// </summary> | |
| /// <returns>The <see cref="T:System.Drawing.Font" /> to apply to the text displayed by the control. The default is the value of the <see cref="P:System.Windows.Forms.Control.DefaultFont" /> property.</returns> | |
| /// <PermissionSet> | |
| /// <IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" /> | |
| /// <IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" /> | |
| /// <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode, ControlEvidence" /> | |
| /// <IPermission class="System.Diagnostics.PerformanceCounterPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" /> | |
| /// </PermissionSet> | |
| public override Font Font { | |
| get { | |
| // Return the base class font. | |
| return base.Font; | |
| } | |
| set { | |
| // Set the base class font... | |
| base.Font = value; | |
| // ...and the internal control font. | |
| richTextBoxConsole.Font = value; | |
| } | |
| } | |
| /// <summary> | |
| /// Gets or sets the background color for the control. | |
| /// </summary> | |
| /// <returns>A <see cref="T:System.Drawing.Color" /> that represents the background color of the control. The default is the value of the <see cref="P:System.Windows.Forms.Control.DefaultBackColor" /> property.</returns> | |
| /// <PermissionSet> | |
| /// <IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" /> | |
| /// </PermissionSet> | |
| public override Color BackColor { | |
| get { | |
| // Return the base class background. | |
| return base.BackColor; | |
| } | |
| set { | |
| // Set the base class background... | |
| base.BackColor = value; | |
| // ...and the internal control background. | |
| richTextBoxConsole.BackColor = value; | |
| } | |
| } | |
| } | |
| /// <summary> | |
| /// Used to allow us to find resources properly. | |
| /// </summary> | |
| public class Resfinder { } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // | |
| // Copyright 2014, Desert Software Solutions Inc. | |
| // ConsoleEventArgs.cs: https://gist.github.com/rostreim/10558496 | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| // | |
| using System; | |
| namespace DesertSoftware.ShellControl | |
| { | |
| /// <summary> | |
| /// The ConsoleEventArgs are arguments for a console event. | |
| /// </summary> | |
| public class ConsoleEventArgs : EventArgs | |
| { | |
| /// <summary> | |
| /// Initializes a new instance of the <see cref="ConsoleEventArgs"/> class. | |
| /// </summary> | |
| public ConsoleEventArgs() { } | |
| /// <summary> | |
| /// Initializes a new instance of the <see cref="ConsoleEventArgs"/> class. | |
| /// </summary> | |
| /// <param name="content">The content.</param> | |
| public ConsoleEventArgs(string content) { | |
| // Set the content. | |
| Content = content; | |
| } | |
| /// <summary> | |
| /// Gets the content. | |
| /// </summary> | |
| public string Content { get; private set; } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // | |
| // Copyright 2014, Desert Software Solutions Inc. | |
| // ProcessEventArgs.cs: https://gist.github.com/rostreim/10558496 | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| // | |
| using System; | |
| namespace DesertSoftware.ShellControl | |
| { | |
| /// <summary> | |
| /// The ProcessEventArgs are arguments for a console event. | |
| /// </summary> | |
| public class ProcessEventArgs : EventArgs | |
| { | |
| /// <summary> | |
| /// Initializes a new instance of the <see cref="ProcessEventArgs"/> class. | |
| /// </summary> | |
| public ProcessEventArgs() { } | |
| /// <summary> | |
| /// Initializes a new instance of the <see cref="ProcessEventArgs"/> class. | |
| /// </summary> | |
| /// <param name="content">The content.</param> | |
| public ProcessEventArgs(string content) { | |
| // Set the content. | |
| Content = content; | |
| } | |
| /// <summary> | |
| /// Initializes a new instance of the <see cref="ProcessEventArgs"/> class. | |
| /// </summary> | |
| /// <param name="code">The code.</param> | |
| public ProcessEventArgs(int code) { | |
| // Set the code. | |
| Code = code; | |
| } | |
| /// <summary> | |
| /// Initializes a new instance of the <see cref="ProcessEventArgs"/> class. | |
| /// </summary> | |
| /// <param name="content">The content.</param> | |
| /// <param name="code">The code.</param> | |
| public ProcessEventArgs(string content, int code) { | |
| // Set the content and code. | |
| Content = content; | |
| Code = code; | |
| } | |
| /// <summary> | |
| /// Gets the content. | |
| /// </summary> | |
| public string Content { get; private set; } | |
| /// <summary> | |
| /// Gets or sets the code. | |
| /// </summary> | |
| /// <value> | |
| /// The code. | |
| /// </value> | |
| public int? Code { get; private set; } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // | |
| // Copyright 2014, Desert Software Solutions Inc. | |
| // ProcessManager.cs: https://gist.github.com/rostreim/10558496 | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| // | |
| using System; | |
| using System.Text; | |
| using System.Diagnostics; | |
| using System.ComponentModel; | |
| using System.IO; | |
| namespace DesertSoftware.ShellControl | |
| { | |
| /// <summary> | |
| /// A ProcessEventHandler is a delegate for process input/output events. | |
| /// </summary> | |
| /// <param name="sender">The sender.</param> | |
| /// <param name="args">The <see cref="ProcessEventArgs"/> instance containing the event data.</param> | |
| public delegate void ProcessEventHandler(object sender, ProcessEventArgs args); | |
| /// <summary> | |
| /// A class that wraps a process providing programmatic input and output. | |
| /// </summary> | |
| public class ProcessManager | |
| { | |
| /// <summary> | |
| /// Initializes a new instance of the <see cref="ProcessManager"/> class. | |
| /// </summary> | |
| public ProcessManager() | |
| { | |
| // Configure the output worker. | |
| outputWorker.WorkerReportsProgress = true; | |
| outputWorker.WorkerSupportsCancellation = true; | |
| outputWorker.DoWork += outputWorker_DoWork; | |
| outputWorker.ProgressChanged += outputWorker_ProgressChanged; | |
| // Configure the error worker. | |
| errorWorker.WorkerReportsProgress = true; | |
| errorWorker.WorkerSupportsCancellation = true; | |
| errorWorker.DoWork += errorWorker_DoWork; | |
| errorWorker.ProgressChanged += errorWorker_ProgressChanged; | |
| } | |
| /// <summary> | |
| /// Handles the ProgressChanged event of the outputWorker control. | |
| /// </summary> | |
| /// <param name="sender">The source of the event.</param> | |
| /// <param name="e">The <see cref="System.ComponentModel.ProgressChangedEventArgs"/> instance containing the event data.</param> | |
| void outputWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) | |
| { | |
| // We must be passed a string in the user state. | |
| if (e.UserState is string) | |
| { | |
| // Fire the output event. | |
| FireProcessOutputEvent(e.UserState as string); | |
| } | |
| } | |
| /// <summary> | |
| /// Handles the DoWork event of the outputWorker control. | |
| /// </summary> | |
| /// <param name="sender">The source of the event.</param> | |
| /// <param name="e">The <see cref="System.ComponentModel.DoWorkEventArgs"/> instance containing the event data.</param> | |
| void outputWorker_DoWork(object sender, DoWorkEventArgs e) | |
| { | |
| while (outputWorker.CancellationPending == false) | |
| { | |
| // Any lines to read? | |
| int count; | |
| var buffer = new char[1024]; | |
| do | |
| { | |
| var builder = new StringBuilder(); | |
| count = outputReader.Read(buffer, 0, 1024); | |
| builder.Append(buffer, 0, count); | |
| outputWorker.ReportProgress(0, builder.ToString()); | |
| } while (count > 0); | |
| System.Threading.Thread.Sleep(200); | |
| } | |
| } | |
| /// <summary> | |
| /// Handles the ProgressChanged event of the errorWorker control. | |
| /// </summary> | |
| /// <param name="sender">The source of the event.</param> | |
| /// <param name="e">The <see cref="System.ComponentModel.ProgressChangedEventArgs"/> instance containing the event data.</param> | |
| void errorWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) | |
| { | |
| // The userstate must be a string. | |
| if (e.UserState is string) | |
| { | |
| // Fire the error event. | |
| FireProcessErrorEvent(e.UserState as string); | |
| } | |
| } | |
| /// <summary> | |
| /// Handles the DoWork event of the errorWorker control. | |
| /// </summary> | |
| /// <param name="sender">The source of the event.</param> | |
| /// <param name="e">The <see cref="System.ComponentModel.DoWorkEventArgs"/> instance containing the event data.</param> | |
| void errorWorker_DoWork(object sender, DoWorkEventArgs e) | |
| { | |
| while (errorWorker.CancellationPending == false) | |
| { | |
| // Any lines to read? | |
| int count; | |
| var buffer = new char[1024]; | |
| do | |
| { | |
| var builder = new StringBuilder(); | |
| count = errorReader.Read(buffer, 0, 1024); | |
| builder.Append(buffer, 0, count); | |
| errorWorker.ReportProgress(0, builder.ToString()); | |
| } while (count > 0); | |
| System.Threading.Thread.Sleep(200); | |
| } | |
| } | |
| /// <summary> | |
| /// Runs a process. | |
| /// </summary> | |
| /// <param name="fileName">Name of the file.</param> | |
| /// <param name="arguments">The arguments.</param> | |
| public void StartProcess(string fileName, string arguments) | |
| { | |
| // Create the process start info. | |
| var processStartInfo = new ProcessStartInfo(fileName, arguments); | |
| // Set the options. | |
| processStartInfo.UseShellExecute = false; | |
| processStartInfo.ErrorDialog = false; | |
| processStartInfo.CreateNoWindow = true; | |
| // Specify redirection. | |
| processStartInfo.RedirectStandardError = true; | |
| processStartInfo.RedirectStandardInput = true; | |
| processStartInfo.RedirectStandardOutput = true; | |
| // Create the process. | |
| process = new Process(); | |
| process.EnableRaisingEvents = true; | |
| process.StartInfo = processStartInfo; | |
| process.Exited += currentProcess_Exited; | |
| // Start the process. | |
| try | |
| { | |
| process.Start(); | |
| } | |
| catch (Exception e) | |
| { | |
| // Trace the exception. | |
| Trace.WriteLine("Failed to start process " + fileName + " with arguments '" + arguments + "'"); | |
| Trace.WriteLine(e.ToString()); | |
| return; | |
| } | |
| // Store name and arguments. | |
| processFileName = fileName; | |
| processArguments = arguments; | |
| // Create the readers and writers. | |
| inputWriter = process.StandardInput; | |
| outputReader = TextReader.Synchronized(process.StandardOutput); | |
| errorReader = TextReader.Synchronized(process.StandardError); | |
| // Run the workers that read output and error. | |
| outputWorker.RunWorkerAsync(); | |
| errorWorker.RunWorkerAsync(); | |
| } | |
| /// <summary> | |
| /// Stops the process. | |
| /// </summary> | |
| public void StopProcess() | |
| { | |
| // Handle the trivial case. | |
| if (IsProcessRunning == false) | |
| return; | |
| // Kill the process. | |
| process.Kill(); | |
| } | |
| /// <summary> | |
| /// Handles the Exited event of the currentProcess control. | |
| /// </summary> | |
| /// <param name="sender">The source of the event.</param> | |
| /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param> | |
| void currentProcess_Exited(object sender, EventArgs e) | |
| { | |
| // Fire process exited. | |
| FireProcessExitEvent(process.ExitCode); | |
| // Disable the threads. | |
| outputWorker.CancelAsync(); | |
| errorWorker.CancelAsync(); | |
| inputWriter = null; | |
| outputReader = null; | |
| errorReader = null; | |
| process = null; | |
| processFileName = null; | |
| processArguments = null; | |
| } | |
| /// <summary> | |
| /// Fires the process output event. | |
| /// </summary> | |
| /// <param name="content">The content.</param> | |
| private void FireProcessOutputEvent(string content) | |
| { | |
| // Get the event and fire it. | |
| var theEvent = OnProcessOutput; | |
| if (theEvent != null) | |
| theEvent(this, new ProcessEventArgs(content)); | |
| } | |
| /// <summary> | |
| /// Fires the process error output event. | |
| /// </summary> | |
| /// <param name="content">The content.</param> | |
| private void FireProcessErrorEvent(string content) | |
| { | |
| // Get the event and fire it. | |
| var theEvent = OnProcessError; | |
| if (theEvent != null) | |
| theEvent(this, new ProcessEventArgs(content)); | |
| } | |
| // Fires the process input event. | |
| private void FireProcessInputEvent(string content) { | |
| var theEvent = OnProcessInput; | |
| if (theEvent != null) | |
| theEvent(this, new ProcessEventArgs(content)); | |
| } | |
| // Fires the process exit event | |
| private void FireProcessExitEvent(int code) { | |
| var theEvent = OnProcessExit; | |
| if (theEvent != null) | |
| theEvent(this, new ProcessEventArgs(code)); | |
| } | |
| /// <summary> | |
| /// Writes the input. | |
| /// </summary> | |
| /// <param name="input">The input.</param> | |
| public void WriteInput(string input) | |
| { | |
| if (IsProcessRunning) | |
| { | |
| inputWriter.WriteLine(input); | |
| inputWriter.Flush(); | |
| } | |
| } | |
| private Process process; // the current process we are managing | |
| private StreamWriter inputWriter; // the writer to which all input is echoed to | |
| private TextReader outputReader; // the stdout reader to which the process is writing | |
| private TextReader errorReader; // the stderr reader to which the process is writing | |
| private BackgroundWorker outputWorker = new BackgroundWorker(); | |
| private BackgroundWorker errorWorker = new BackgroundWorker(); | |
| private string processFileName; | |
| private string processArguments; | |
| /// <summary> | |
| /// Occurs when the process writes to stdout | |
| /// </summary> | |
| public event ProcessEventHandler OnProcessOutput; | |
| /// <summary> | |
| /// Occurs when the process writes to stderr | |
| /// </summary> | |
| public event ProcessEventHandler OnProcessError; | |
| /// <summary> | |
| /// Occurs when input is received from the process. | |
| /// </summary> | |
| public event ProcessEventHandler OnProcessInput; | |
| /// <summary> | |
| /// Occurs when the process ends. | |
| /// </summary> | |
| public event ProcessEventHandler OnProcessExit; | |
| /// <summary> | |
| /// Gets a value indicating whether this instance is process running. | |
| /// </summary> | |
| /// <value> | |
| /// <c>true</c> if this instance is process running; otherwise, <c>false</c>. | |
| /// </value> | |
| public bool IsProcessRunning { | |
| get { | |
| try { | |
| return (this.process != null && !this.process.HasExited); | |
| } catch { | |
| return false; | |
| } | |
| } | |
| } | |
| /// <summary> | |
| /// Gets the active process instance | |
| /// </summary> | |
| public Process Process { | |
| get { return this.process; } | |
| } | |
| /// <summary> | |
| /// Gets the file name of the process. | |
| /// </summary> | |
| public string ProcessFileName { | |
| get { return this.processFileName; } | |
| } | |
| /// <summary> | |
| /// Gets the process arguments. | |
| /// </summary> | |
| public string ProcessArguments | |
| { | |
| get { return this.processArguments; } | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment