Link to home
Start Free TrialLog in
Avatar of jlspencer
jlspencer

asked on

on going issues wtih C# and robocopy

Here is my code, the robocopy portion works fine when run from the command line (slightly changed) but not within the c# code.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net.NetworkInformation;
using System.IO;
using System.Diagnostics;



namespace MyUSB_Backup
{
   

    public partial class Form1 : Form
    {
       

        public Form1()
        {
           
            InitializeComponent();
           
        }

        private void Backup_button_Click(object sender, EventArgs e)

        {
           
            Resultlabel.Text = ("Checking for access to network folder");

            //  3 second pause
            DateTime Tthen = DateTime.Now;
            do
            {
                Application.DoEvents();
            } while (Tthen.AddSeconds(3) > DateTime.Now);
           

            //string to verify connection to network
            //This will go away when user selection is added
            string strFullPath = @"\\SERVER\\DATA\HOME\\USER\\USER-FOLDER";
           
            if (!Directory.Exists(strFullPath))
           
            {
                //if folder not found message.
                Resultlabel.Text = ("Please connect to network or VPN");
               
            }

            else
            {

                //This message never displays, not sure why.
                Resultlabel.Text = ("Ok to backup files");
                ProcessStartInfo startInfo = new ProcessStartInfo();

                //USB Drive Letter,  later this will be users choice of FROM location

                startInfo.WorkingDirectory = "E:";



                //Later User will choose  TO location to place files using browse;

                startInfo.FileName = "robocopy.exe";
                startInfo.Arguments = "\"\\USER-FOLDER \\SERVER\\DATA\\Home\\USER\\USERFOLDER /xo /s /Z /W:5";
                startInfo.UseShellExecute = false;

                 //commenting out the below code allows a window to display but flashes so quick you cannot see anything
                // Not sure how to pause this window
                //startInfo.CreateNoWindow = true;
                startInfo.RedirectStandardError = true;
                startInfo.RedirectStandardOutput = true;
                Process p = Process.Start(startInfo);
                string errors = p.StandardError.ReadToEnd();
                string output = p.StandardOutput.ReadToEnd();
               

                p.WaitForExit();
                Resultlabel.Text = ("Backup Complete");
           

             }

        }

        private void Exitbutton_Click(object sender, EventArgs e)
        {
            Application.Exit();

        }
    }
    }
Avatar of William Domenz
William Domenz
Flag of United States of America image

Can you step through the code, stop after it creates all the start info objects, then copy them into a bat file and try to run it?
This should allow us to see what robocopy is complaining about outside the c# code
How about this instead (going to take a little work but this will at least give us some output for your command):

1. Create a new C# Windows Forms project

I named my project EE_Q28794139

2. Add a new user control

User generated imageUser generated image

3. Modify the code files for your newly created controls

CommandWindowRichTextBox.Designer.cs -
namespace EE_Q28794139
{
	partial class CommandWindowRichTextBox
	{
		/// <summary> 
		/// Required designer variable.
		/// </summary>
		private System.ComponentModel.IContainer components = null;

		/// <summary> 
		/// Clean up any resources being used.
		/// </summary>
		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
		protected override void Dispose(bool disposing)
		{
			if (disposing && (components != null))
			{
				components.Dispose();
			}
			base.Dispose(disposing);
		}

		#region Component Designer generated code

		/// <summary> 
		/// Required method for Designer support - do not modify 
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
			components = new System.ComponentModel.Container();
		}

		#endregion
	}
}

Open in new window

CommandWindowRichTextBox.cs -
using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Text;
using System.Threading;
using System.Windows.Forms;

namespace EE_Q28794139
{
	public partial class CommandWindowRichTextBox : RichTextBox
	{
		private EventHandler<EventArgs> fProcessCompleted;
		public event EventHandler<EventArgs> ProcessCompleted
		{
			add { fProcessCompleted += value; }
			remove { fProcessCompleted -= value; }
		}

		protected virtual void OnProcessCompleted(object sender, EventArgs e)
		{
			EventHandler<EventArgs> handler = fProcessCompleted;
			if (handler != null)
				handler(sender, e);
			fProcessIOManager.StopMonitoringProcessOutput();
		}

		// Windows message codes to intercept keystrokes in the overridden WndProc() method
		/// <summary>The windows message character constant</summary>
		private const int WM_CHAR = 0x102;
		/// <summary>The windows message keydown constant</summary>
		private const int WM_KEYDOWN = 0x100;
		/// <summary>The windows message keyup constant</summary>
		private const int WM_KEYUP = 0x101;

		/// <summary>Index into the controls text where the user input starts (All prior text will be either stdout/stderr text from the running process)</summary>
		private int fIndexStartInputPosition;

		/// <summary>Index into the controls text area where the user is currently typing. This index will always be great than the idxStartInputPostion index.</summary>
		/// <remarks>The user input text will be from 'idxStartInputPosition' to 'idxCurrentInputPosition'</remarks>
		private int fIndexCurrentInputPosition;

		// Process that is being executed and monitored for stdout/stdin
		//private Process process;

		/// <summary>Class that asynchronously monitors the running process, and notifies this textbox control when stdout/stderr text is read.</summary>
		private IIoManager fProcessIOManager;

		/// <summary>Convenience array used to pass in parameters via the .Invoke() method</summary>
		private readonly object[] fSingleArgumentArray;

		/// <summary>Ignore stdout matching the last input string to running process stdin</summary>
		private bool fIgnoreOutputTextMatchingLastInput = true;

		/// <summary>Last string that was sent to the standard input of the running process</summary>
		private string fLastInputTextSentToProcess = "";

		// Delegate declaration, and delegate instance declarations to allow the appending of text to the rich text box.
		// Note: these delegates are invoked (using the this.Invoke() method) so that they run on the rich text box's gui thread.
		/// <summary>Delegate PutTextCallBackDelegate</summary>
		/// <param name="text">The text.</param>
		private delegate void PutTextCallBackDelegate(string text);
		/// <summary>The put standard out text delegate</summary>
		private readonly PutTextCallBackDelegate fPutStdOutTextDelegate;
		/// <summary>The put standard error text delegate</summary>
		private readonly PutTextCallBackDelegate fPutStdErrTextDelegate;

		#region Public_Behavioral_Properties_And_Events
		/// <summary>Gets or sets a value indicating whether ignore the output text string that matches the last string sent to the running process' standard input stream. This can happen with 
		/// some command (such as 'cmd.exe') that echos back to stdout the text that it received as a command on stdin.</summary>
		/// <value><c>true</c> if ignore standard output text matching last standard input; otherwise, <c>false</c>.</value>
		public bool IgnoreOutputTextMatchingLastInput
		{
			get { return fIgnoreOutputTextMatchingLastInput; }
			set { fIgnoreOutputTextMatchingLastInput = value; }
		}

		// Event to notify receiver of a string read from stdout stream
		/// <summary>Occurs when [stdout text read].</summary>
		public event EventHandler<IoOutputEventArgs> StdOutTextRead;

		// Event to notify receiver of a string read from stderr stream
		/// <summary>Occurs when [stderr text read].</summary>
		public event EventHandler<IoOutputEventArgs> StdErrTextRead;
		#endregion

		#region Consructors_And_Initializaation
		/// <summary>Initializes a new instance of the <see cref="CommandWindowRichTextBox"/> class.</summary>
		public CommandWindowRichTextBox()
		{
			InitializeComponent();
			fPutStdOutTextDelegate = AppendStdOutText;
			fPutStdErrTextDelegate = AppendStdErrText;
			fSingleArgumentArray = new object[] { 0 };
			Multiline = true;
		}
		#endregion

		#region Process_Interaction
		/// <summary>Gets or sets the executing process for standard output/standard error monitoring.</summary>
		/// <value>The executing process.</value>
		public Process ExecutingProcess
		{
			get
			{
				if (fProcessIOManager == null)
					return null;
				return fProcessIOManager.Process;
			}
			set
			{
				Clear();

				// Stop any background threads for reading
				if (fProcessIOManager != null)
				{
					// Stop background threads, etc.
					fProcessIOManager.StopMonitoringProcessOutput();
					fProcessIOManager = null;
				}

				if (value != null)
				{
					// Create a new ProcessIoMgr object, pass in the supplied executing process, and set events to 
					// receive notification of stdout and stderr text output from the process.
					fProcessIOManager = new ProcessIoManager(value);
					fProcessIOManager.StdErrTextRead += OnStdErrTextRead;
					fProcessIOManager.StdOutTextRead += OnStdOutTextRead;
					fProcessIOManager.ProcessCompleted += OnProcessCompleted;
					// Start the individual threads to monitor process text output
					fProcessIOManager.StartProcessOutputRead();
				}
			}
		}

		/// <summary>Called when [stdout text read].</summary>
		/// <param name="sender">event sender</param>
		/// <param name="args">event args, which includes stdout text</param>
		private void OnStdOutTextRead(object sender, IoOutputEventArgs args)
		{
			fSingleArgumentArray[0] = args.Text;
			try
			{
				// Use this.Invoke() to call delegate to prevent cross
				// threading exceptions.
				// This method will put the supplied text into the
				// rich text box.
				Invoke(fPutStdOutTextDelegate, fSingleArgumentArray);
			}
			catch (ObjectDisposedException)
			{
				// Can happen when the application shuts down, the windows
				// get destroyed, but another thread calls this method.
				// The attempt is then made to write to the window 
				// which as been destoryed.             
				fProcessIOManager.StopMonitoringProcessOutput();
			}
			catch (Exception ex)
			{
				Console.WriteLine("Exception reported in {0} - {1} [{2}]", ex.Source, ex, ex.Message);
			}
		}

		/// <summary>Called when [stderr text read].</summary>
		/// <param name="sender">event sender</param>
		/// <param name="args">event args, which includes stderr text</param>
		private void OnStdErrTextRead(object sender, IoOutputEventArgs args)
		{
			fSingleArgumentArray[0] = args.Text;
			try
			{
				Invoke(fPutStdErrTextDelegate, fSingleArgumentArray);
			}
			catch (ObjectDisposedException)
			{
				fProcessIOManager.StopMonitoringProcessOutput();
			}
			catch (Exception ex)
			{
				Console.WriteLine("Exception reported in {0} - {1} [{2}]", ex.Source, ex, ex.Message);
			}
		}
		#endregion

		#region Public_Properties
		/// <summary>Get the portion of text that is the user input text (Should be the text at the end of the base.Text string)</summary>
		/// <value>The input text.</value>
		public string InputText
		{
			get { return fIndexStartInputPosition >= 0 && fIndexCurrentInputPosition > fIndexStartInputPosition ? Text.Substring(fIndexStartInputPosition) : ""; }
		}

		/// <summary>Gets the output text (without the InputText).</summary>
		/// <value>The output text.</value>
		public string OutputText
		{
			get
			{
				if (fIndexStartInputPosition > 0)
					return base.Text.Substring(0, fIndexStartInputPosition);
				return base.Text;
			}
		}

		/// <summary>Gets or sets the current text in the <see cref="T:System.Windows.Forms.TextBox" />.</summary>
		/// <value>The text.</value>
		/// <returns>The text displayed in the control.</returns>
		public override string Text
		{
			// Note: this property is called from base.Clear()
			get
			{
				// Still returns everything in text box (input/and output text)
				return base.Text;
			}
			set
			{
				// Replace the entire text - erase input text as well.
				ClearInputTextIndicies();
				base.Text = ScrubText(value);

				if (string.IsNullOrEmpty(value))
					// The control is being cleared.
					ZeroTextPositions();
			}
		}
		#endregion

		/// <summary>Clears all text from the text box control</summary>
		/// <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 new void Clear()
		{
			ZeroTextPositions();
			base.Clear();
		}

		/// <summary>Writes the supplied text to the standard input of the running process' standard input stream.</summary>
		/// <param name="inputText">The input text string to write to the running process' standard input stream.</param>
		public void WriteTextToProcessStdIn(string inputText)
		{
			if (fProcessIOManager != null && ExecutingProcess != null && ExecutingProcess.HasExited == false && string.IsNullOrEmpty(inputText) == false)
			{
				// Save the text that we're sending
				fLastInputTextSentToProcess = inputText;

				// Adjust indexing positions                             
				fIndexStartInputPosition = GetStartOfInputIndex();
				fIndexCurrentInputPosition = fIndexStartInputPosition;
				SelectionStart = fIndexStartInputPosition;

				Debug.WriteLine(string.Format("Sending Text [{0}]", inputText));

				// Perform acutal stdin write
				fProcessIOManager.WriteStdIn(inputText);
			}
		}

		/// <summary>The TextBox appears to eliminate the '\r' character when manipulating text, so just make sure to eliminate it before doing any other manipulation internally.</summary>
		/// <param name="text">The text string to scrub.</param>
		/// <returns>Scrubbed text</returns>
		private string ScrubText(string text)
		{
			string result = text;
			if (result != null)
				result = result.Replace("\r", "");
			return result;
		}

		//public void AppendInputText(string inputText)
		//{
		//    inputText = ScrubText(Text);

		//    string newInputText = InputText + inputText;

		//    // Initialize starting input position if it is not already initialized
		//    if (idxStartInputPosition < 0)
		//        idxStartInputPosition = GetStartOfInputIndex();

		//    // Append the input text to the end of the text on screen
		//    base.Text += newInputText;

		//    // Update the current input text position
		//    idxCurrentInputPosition = idxStartInputPosition + inputText.Length;

		//    // Position the cursor at the end for the text
		//    SelectionStart = idxCurrentInputPosition;
		//    this.ScrollToCaret();
		//}


		/// <summary>Appends the given text (standard output text from running process), to the text displayed in this text box.</summary>
		/// <param name="outputText">The stdout output text of the running process.</param>
		private void AppendStdOutText(string outputText)
		{
			if (fIgnoreOutputTextMatchingLastInput && fLastInputTextSentToProcess.Length > 0 && ScrubText(outputText).Trim() == ScrubText(fLastInputTextSentToProcess).Trim())
			{
				// The running process has echoed the text that was it was last sent to it (which should be displaying in the text box)
				// Since it is already displayed, then don't repeat the text. The only thing to output is a newline character, to get the text box start displaying subsequent text on the next 
				// line (if the text to display ends in a newline character)
				// (This behavior can be observed with the 'cmd.exe' command.
				Debug.WriteLine(string.Format("Ignoring last input text:{0}", ScrubText(fLastInputTextSentToProcess)));
				if (outputText.EndsWith("\n"))
					AppendOutputText("\n", true);
			}
			else
				AppendOutputText(outputText, true);

			fLastInputTextSentToProcess = "";
		}

		/// <summary>Appends the given text (standard error text from running process), to the text displayed
		/// in this text box.</summary>
		/// <param name="outputText">The stderr output text of the running process.</param>
		private void AppendStdErrText(string outputText)
		{
			AppendOutputText(outputText, false);
		}


		/// <summary>Appends the output text of a running process, either stdout or stderr text,
		/// to the control.</summary>
		/// <param name="outputText">The output text from the running process.</param>
		/// <param name="isStdout">if set to <c>true</c> the text is stdout text, otherwise stderr text is assumed.</param>
		private void AppendOutputText(string outputText, bool isStdout)
		{
			if (string.IsNullOrEmpty(outputText))
				return;

			outputText = ScrubText(outputText);

			string currentOutputText = OutputText;
			string currentInputText = InputText;

			if (currentInputText.Length > 0)
			{
				// Erase the input text: This is done because it is too much overhead to do:  Text += currentOutputText + currentInputText
				// Way too much flicker and performance degredation - better to do an append of the text we need to add
				ClearInputTextIndicies();
				Text = currentOutputText;
			}

			AppendText(outputText);
			fIndexStartInputPosition = GetStartOfInputIndex();
			fIndexCurrentInputPosition = fIndexStartInputPosition;
			SelectionStart = fIndexStartInputPosition;

			ScrollToCaret();

			// Notify via events of stream text read
			if (isStdout && StdOutTextRead != null)
			{
				var eh = StdOutTextRead;
				if (eh != null)
					eh(this, new IoOutputEventArgs(outputText));
			}
			else if (isStdout == false && StdErrTextRead != null)
			{
				var eh = StdErrTextRead;
				if (eh != null)
					eh(this, new IoOutputEventArgs(outputText));
			}
		}

		/// <summary>Sets the SelectionStart, idxStartInputPosition, and idxCurrentInputPosition to zero.</summary>
		private void ZeroTextPositions()
		{
			SelectionStart = 0;
			fIndexStartInputPosition = 0;
			fIndexCurrentInputPosition = 0;
		}

		/// <summary>Sets idxStartInputPosition and idxCurrentInputPosition indicies to '-1'</summary>
		private void ClearInputTextIndicies()
		{
			fIndexStartInputPosition = -1;
			fIndexCurrentInputPosition = -1;
		}

		/// <summary>Returns the next position for the user to enter text.
		/// This value is the Text.Length value. The 'Text' being displayed is the
		/// stdout/stderr output of running processes.</summary>
		/// <returns>Next position in this textbox for user input</returns>
		private int GetStartOfInputIndex()
		{
			int result = Text.Length;
			return result;
		}

		#region KEYBOARD_HANDLING_METHODS_AND_OVERRIDES
		/// <summary>Used for detecting CONTROL-C, or other combinations</summary>
		/// <remarks>true if the control key has been pressed (key down), and false when it is released (key up)</remarks>
		private bool fControlKeyPressed;

		/// <summary>Override of base WndProc method, to custom handle
		/// WM_KEYDOWN, WM_CHAR, and WM_KEYUP messages.</summary>
		/// <param name="m">A Windows Message object.</param>
		protected override void WndProc(ref Message m)
		{
			bool handled = false;
			if (m.Msg == WM_KEYDOWN || m.Msg == WM_CHAR || m.Msg == WM_KEYUP)
			{
				if (ProcessKey((int)m.WParam, m.Msg) == false)
					handled = true;
			}

			if (handled)
			{
				// Eat the message!
				m.Msg = 0;
				m.WParam = IntPtr.Zero;
				m.LParam = IntPtr.Zero;
			}
			else
				base.WndProc(ref m);
		}

		/// <summary>Figure out if the given key should be processed:
		/// Options:
		/// (1) Do not process the key, if an attempt in being made to modify
		/// the text prior to the set input position. (caret position is less
		/// than the set input position)
		/// (2) Allow processing of arrow keys, so user can move around the
		/// screen, regardless of the position
		/// (3) For BACKSPACE key, only allow a back space if the user is in the
		/// input portion of the text (i.e. - end of text) - caret position is
		/// less than or equal to the starting input position.</summary>
		/// <param name="keycode">The keycode.</param>
		/// <param name="msg">Message value from WndProc.</param>
		/// <returns>Returns true if the message should be handled by default processing, false if the message is locally handled</returns>
		private bool ProcessKey(int keycode, int msg)
		{
			bool handleKeyEvent = true;

			//Console.WriteLine("ProcessKeyDown: SelectionStart=" + SelectionStart + ", idxCurrent=" + idxCurrentInputPosition + ", idxStart=" + idxStartInputPosition);
			switch (keycode)
			{
				case (int)Keys.Left:
				case (int)Keys.Right:
				case (int)Keys.Down:
				case (int)Keys.Up:
				case (int)Keys.Home:
				case (int)Keys.End:
				case (int)Keys.PageDown:
				case (int)Keys.PageUp:
					// Allow the movement keys around the box (no modifications to text for these)
					break;

				case (int)Keys.ControlKey:
					if (msg == WM_KEYUP)
						fControlKeyPressed = false;
					else if (msg == WM_KEYDOWN)
						fControlKeyPressed = true;
					break;

				case (int)Keys.Back:
					if (SelectionStart <= fIndexStartInputPosition)
					{
						// Do not allow the backspace key to go beyond the input starting position and erase output text
						handleKeyEvent = false;
					}
					break;

				case (int)Keys.C:
				case (int)Keys.A:
					if (fControlKeyPressed)
					{
						// Do nothing - Allow normal processing of a CONTROL-C (Copy),
						// or a CONTROL-A (Select all)
						Console.WriteLine(string.Format("CONTROL-{0} !!!", ((char)keycode).ToString(CultureInfo.InvariantCulture).ToUpper()));
					}
					else
					{
						// Perform the default processing for keys pressed. (See also default: section)
						if (SelectionStart < fIndexStartInputPosition)
							handleKeyEvent = false;
					}
					break;

				case (int)Keys.Enter: // (Same as 'Keys.Return')
					// User has pressed enter/return - Send input text to the running process
					if (msg == WM_KEYUP)
					{
						// This handling was done more for 'cmd.exe' than anything else. If a "\n" character is sent to cmd.exe, the that process sends back two prompts, instead of the 
						// expected one prompt. Therefore - do not send a newline character to that process, but just send a blank.
						//
						// NOTE: This may have to be adjusted for other types of command line processes, and what the expect to receive (newlines, etc).
						WriteTextToProcessStdIn(InputText.Length <= 0 ? " " : InputText);
					}
					handleKeyEvent = false;
					break;

				default:
					// For all other keys (should be input keys, etc),
					// Make sure the the modification operation is not 
					// prior to the input starting position.
					if (SelectionStart < fIndexStartInputPosition)
						handleKeyEvent = false;
					break;
			}
			return handleKeyEvent;
		}

		/// <summary>Raises the <see cref="E:System.Windows.Forms.Control.KeyDown" /> event.</summary>
		/// <param name="e">A <see cref="T:System.Windows.Forms.KeyEventArgs" /> that contains the event data.</param>
		protected override void OnKeyDown(KeyEventArgs e)
		{
			//Console.WriteLine("OnKeyDown");
			//Console.WriteLine("SelectionStart=" + SelectionStart + ", idxCurrent=" + idxCurrentInputPosition + ", idxStart=" + idxStartInputPosition);
			base.OnKeyDown(e);
			fIndexCurrentInputPosition = SelectionStart;
		}

		/// <summary>Raises the <see cref="E:System.Windows.Forms.Control.KeyUp" /> event.</summary>
		/// <param name="e">A <see cref="T:System.Windows.Forms.KeyEventArgs" /> that contains the event data.</param>
		protected override void OnKeyUp(KeyEventArgs e)
		{
			//Console.WriteLine("OnKeyUp");
			//Console.WriteLine("SelectionStart=" + SelectionStart + ", idxCurrent=" + idxCurrent + ", idxStart=" + idxStart);
			base.OnKeyUp(e);
			fIndexCurrentInputPosition = SelectionStart;
		}

		/// <summary>Raises the <see cref="E:System.Windows.Forms.Control.KeyPress" /> event.</summary>
		/// <param name="e">A <see cref="T:System.Windows.Forms.KeyPressEventArgs" /> that contains the event data.</param>
		protected override void OnKeyPress(KeyPressEventArgs e)
		{
			//Console.WriteLine("OnKeyPress");
			//Console.WriteLine("SelectionStart=" + SelectionStart + ", idxCurrent=" + idxCurrentInputPosition + ", idxStart=" + idxStartInputPosition);
			base.OnKeyPress(e);
			fIndexCurrentInputPosition = SelectionStart;
		}
		#endregion
	}

	/// <summary>Class that manages the reading of the output produced by a given 'Process' and reports the output via events.
	/// <p>Both standard error (stderr) and standard output (stdout) are managed and reported.</p>
	/// The stdout and stderr monitoring and reading are each performed by separate background threads. Each thread blocks on a Read()
	/// method, waiting for text in the stream being monitored to become available.</summary>
	/// <remarks>Note the Process.RedirectStandardOutput must be set to true in order to read standard output from the process, and the Process.RedirectStandardError
	/// must be set to true to read standard error.</remarks>
	public class ProcessIoManager : IIoManager
	{
		private EventHandler<EventArgs> fProcessCompleted;
		public event EventHandler<EventArgs> ProcessCompleted
		{
			add { fProcessCompleted += value; }
			remove { fProcessCompleted -= value; }
		}

		protected virtual void OnProcessCompleted(object sender, EventArgs e)
		{
			EventHandler<EventArgs> handler = fProcessCompleted;
			if (handler != null)
				handler(sender, e);
		}

		#region Private_Fields
		/// <summary>The command line process that is executing and being monitored by this class for stdout/stderr output.</summary>
		private Process fProcess;

		/// <summary>The thread to monitor and read standard output (stdout).</summary>
		private Thread fStdOutThread;

		/// <summary>The thread to monitor and read standard error (stderr).</summary>
		private Thread fStdErrThread;

		/// <summary>The buffer to hold characters read from either stdout, or stderr streams.</summary>
		private readonly StringBuilder fStreamBuffer;
		#endregion

		#region Public_Properties_And_Events
		/// <summary>Gets the process being monitored</summary>
		/// <value>The running process.</value>
		/// <exception cref="System.ArgumentException">Cannot set Process after it has been initially set.</exception>
		public Process Process
		{
			get { return fProcess; }
			set
			{
				if (fProcess != null)
					throw new ArgumentException("Cannot set Process after it has been initially set.");
				fProcess = value;
			}
		}

		/// <summary>Event to notify of a string read from stdout stream.</summary>
		public event EventHandler<IoOutputEventArgs> StdOutTextRead;

		/// <summary>Event to notify of a string read from stderr stream.</summary>
		public event EventHandler<IoOutputEventArgs> StdErrTextRead;

		#endregion

		#region Constructor_And_Initialization

		/// <summary>Initializes a new instance of the <see cref="ProcessIoManager" /> class.</summary>
		/// <seealso cref="StartProcessOutputRead" />
		public ProcessIoManager()
		{
			fStreamBuffer = new StringBuilder(256);
		}

		/// <summary>Initializes a new instance of the <see cref="ProcessIoManager" /> class.</summary>
		/// <param name="process">The process.</param>
		/// <exception cref="System.Exception">
		/// ProcessIoManager unable to set running process - null value is supplied
		/// or
		/// ProcessIoManager unable to set running process - the process has already existed.
		/// </exception>
		/// <seealso cref="StartProcessOutputRead" />
		/// <remarks>Does not automatically start listening for stdout/stderr.
		/// Call StartProcessOutputRead() to begin listening for process output.</remarks>
		public ProcessIoManager(Process process)
		{
			if (process == null)
				throw new Exception("ProcessIoManager unable to set running process - null value is supplied");

			if (process.HasExited)
				throw new Exception("ProcessIoManager unable to set running process - the process has already existed.");

			fProcess = process;

			if (fProcess.EnableRaisingEvents)
				fProcess.Exited += OnProcessCompleted;

			fStreamBuffer = new StringBuilder(256);
		}
		#endregion

		#region Public_Methods
		/// <summary>Starts the background threads reading any output produced (standard output, standard error)
		/// that is produces by the running process.</summary>
		public void StartProcessOutputRead()
		{
			// Just to make sure there aren't previous threads running.
			StopMonitoringProcessOutput();

			// Make sure we have a valid, running process
			CheckForValidProcess("Unable to start monitoring process output.", true);

			// If the stdout is redirected for the process, then start
			// the stdout thread, which will manage the reading of stdout from the
			// running process, and report text read via supplied events.
			if (fProcess.StartInfo.RedirectStandardOutput)
			{
				fStdOutThread = new Thread(ReadStandardOutputThreadMethod) { IsBackground = true };
				// Make thread a background thread - if it was foreground, then
				// the thread could hang up the process from exiting. Background
				// threads will be forced to stop on main thread termination.
				fStdOutThread.Start();
			}

			// If the stderr  is redirected for the process, then start
			// the stderr thread, which will manage the reading of stderr from the
			// running process, and report text read via supplied events.
			if (fProcess.StartInfo.RedirectStandardError)
			{
				fStdErrThread = new Thread(ReadStandardErrorThreadMethod) { IsBackground = true };
				fStdErrThread.Start();
			}
		}

		/// <summary>Writes the supplied text string to the standard input (stdin) of the running process</summary>
		/// <param name="text">The text to write to running process input stream.</param>
		/// <remarks>In order to be able to write to the Process, the StartInfo.RedirectStandardInput must be set to true.</remarks>
		public void WriteStdIn(string text)
		{
			// Make sure we have a valid, running process
			CheckForValidProcess("Unable to write to process standard input.", true);
			if (fProcess.StartInfo.RedirectStandardInput)
				fProcess.StandardInput.WriteLine(text);
		}

		/// <summary>Stops both the standard input and standard error background reader threads. Will Join
		/// the thread for the specified timeout milliseconds, and Abort if thread is still
		/// active after timeout.</summary>
		/// <param name="timeoutMs">The timeout ms.</param>
		public void StopMonitoringProcessOutput(int timeoutMs = 2000)
		{
			bool terminated = false;

			// Stop the stdout reader thread
			try
			{
				if (fStdOutThread != null)
				{
					terminated = fStdOutThread.Join(timeoutMs);
					if (!terminated)
						fStdOutThread.Abort();
				}
			}
			catch (Exception ex)
			{
				Console.WriteLine("Exception reported in {0} on stopping stdout thread - {1} [{2}]", ex.Source, ex, ex.Message);
			}

			// Stop the stderr reader thread
			try
			{
				if (fStdErrThread != null)
				{
					terminated = fStdErrThread.Join(timeoutMs);
					if (!terminated)
						fStdOutThread.Abort();
				}
			}
			catch (Exception ex)
			{
				Console.WriteLine("Exception reported in {0} on stopping stderr thread - {1} [{2}]", ex.Source, ex, ex.Message);
			}
			fStdOutThread = null;
			fStdErrThread = null;
		}
		#endregion

		#region Private_Methods
		/// <summary>Checks for valid (non-null Process), and optionally check to see if the process has exited.
		/// Throws Exception if process is null, or if process has existed and checkForHasExited is true.</summary>
		/// <param name="errorMessageText">The error message text to display if an exception is thrown.</param>
		/// <param name="checkForHasExited">if set to <c>true</c> [check if process has exited].</param>
		/// <exception cref="System.Exception">
		/// </exception>
		private void CheckForValidProcess(string errorMessageText, bool checkForHasExited)
		{
			errorMessageText = (errorMessageText == null ? "" : errorMessageText.Trim());
			if (fProcess == null)
				throw new Exception(string.Format("{0} (Running process must be available)", errorMessageText));

			if (checkForHasExited && fProcess.HasExited)
				throw new Exception(string.Format("{0} (Process has exited)", errorMessageText));
		}

		/// <summary>Read characters from the supplied stream, and accumulate them in the
		/// 'streambuffer' variable until there are no more characters to read.</summary>
		/// <param name="firstCharRead">The first character that has already been read.</param>
		/// <param name="streamReader">The stream reader to read text from.</param>
		/// <param name="isstdout">if set to <c>true</c> the stream is assumed to be standard output,
		/// otherwise assumed to be standard error.</param>
		private void ReadStream(int firstCharRead, StreamReader streamReader, bool isstdout)
		{
			// One of the streams (stdout, stderr) has characters ready to be written
			// Flush the supplied stream until no more characters remain to be read.
			// The synchronized/ locked section of code to prevent the other thread from
			// reading its stream at the same time, producing intermixed stderr/stdout results. 
			// If the threads were not synchronized, the threads
			// could read from both stream simultaneously, and jumble up the text with
			// stderr and stdout text intermixed.
			lock (this)
			{
				// Clear the stream buffer to hold the text to be read
				fStreamBuffer.Length = 0;

				//Console.WriteLine("CHAR=" + firstCharRead);
				fStreamBuffer.Append((char)firstCharRead);

				// While there are more characters to be read
				while (streamReader.Peek() > -1)
				{
					// Read the character in the queue
					int ch = streamReader.Read();

					// Accumulate the characters read in the stream buffer
					fStreamBuffer.Append((char)ch);

					// Send text one line at a time - much more efficient than
					// one character at a time
					if (ch == '\n')
						NotifyAndFlushBufferText(fStreamBuffer, isstdout);
				}
				// Flush any remaining text in the buffer
				NotifyAndFlushBufferText(fStreamBuffer, isstdout);
			} // End lock()
		}

		/// <summary>Invokes the OnStdoutTextRead (if isstdout==true)/ OnStderrTextRead events
		/// with the supplied streambuilder 'textbuffer', then clears
		/// textbuffer after event is invoked.</summary>
		/// <param name="textbuffer">The textbuffer containing the text string to pass to events.</param>
		/// <param name="isstdout">if set to true, the stdout event is invoked, otherwise stedrr event is invoked.</param>
		private void NotifyAndFlushBufferText(StringBuilder textbuffer, bool isstdout)
		{
			if (textbuffer.Length > 0)
			{
				if (isstdout && StdOutTextRead != null)
				{   // Send notificatin of text read from stdout
					var eh = StdOutTextRead;
					if (eh != null)
						eh(this, new IoOutputEventArgs(textbuffer.ToString()));
				}
				else if (isstdout == false && StdErrTextRead != null)
				{   // Send notificatin of text read from stderr
					var eh = StdErrTextRead;
					if (eh != null)
						eh(this, new IoOutputEventArgs(textbuffer.ToString(), true));
				}
				// 'Clear' the text buffer
				textbuffer.Length = 0;
			}
		}

		/// <summary>Method started in a background thread (stdoutThread) to manage the reading and reporting of
		/// standard output text produced by the running process.</summary>
		private void ReadStandardOutputThreadMethod()
		{
			// Main entry point for thread - make sure the main entry method
			// is surrounded with try catch, so an uncaught exception won't
			// screw up the entire application
			try
			{
				// character read from stdout
				int ch;

				// The Read() method will block until something is available
				while (fProcess != null && (ch = fProcess.StandardOutput.Read()) > -1)
				{
					// a character has become available for reading block the other thread and process this stream's input.
					ReadStream(ch, fProcess.StandardOutput, true);
				}
			}
			catch (Exception ex)
			{
				Console.WriteLine("Exception reported in {0} - {1} [{2}]", ex.Source, ex, ex.Message);
			}
			//finally
			//{
			//     if (fProcess != null && !ProcessChecker.IsProcessRunning(fProcess.ProcessName))
			//          OnProcessCompleted(this, new EventArgs());
			//}
		}

		/// <summary>Method started in a background thread (stderrThread) to manage the reading and reporting of
		/// standard error text produced by the running process.</summary>
		private void ReadStandardErrorThreadMethod()
		{
			try
			{
				// Character read from stderr
				int ch;
				// The Read() method will block until something is available
				while (fProcess != null && (ch = fProcess.StandardError.Read()) > -1)
				{
					// a character has become available for reading block the other thread and process this stream's input.
					ReadStream(ch, fProcess.StandardError, false);
				}
			}
			catch (Exception ex)
			{
				Console.WriteLine("Exception reported in {0} - {1} [{2}]", ex.Source, ex, ex.Message);
			}
			//finally
			//{
			//     if (fProcess != null && !ProcessChecker.IsProcessRunning(fProcess.ProcessName))
			//          OnProcessCompleted(this, new EventArgs());
			//}
		}
		#endregion
	}

	/// <summary>Interface IIoManager</summary>
	public interface IIoManager
	{
		/// <summary>The process to be monitored</summary>
		/// <value>The process.</value>
		Process Process { get; set; }

		/// <summary>Notify of a line of output from stdout.</summary>
		event EventHandler<IoOutputEventArgs> StdOutTextRead;

		/// <summary>Notify of a line of output from stderr.</summary>
		event EventHandler<IoOutputEventArgs> StdErrTextRead;

		/// <summary>Notify of the process completing.</summary>
		event EventHandler<EventArgs> ProcessCompleted;

		/// <summary>Starts the background threads reading any output produced (standard output, standard error) that is produced by the running process.</summary>
		void StartProcessOutputRead();

		/// <summary>Writes the supplied text string to the standard input (stdin) of the running process</summary>
		/// <param name="text">The text to write to running process input stream.</param>
		/// <remarks>In order to be able to write to the Process, the StartInfo.RedirectStandardInput must be set to true.</remarks>
		void WriteStdIn(string text);

		/// <summary>Stops both the standard input and standard error background reader threads. Will Join the thread for the specified timeout milliseconds, and Abort if thread is still active
		/// after timeout.</summary>
		/// <param name="timeoutMs">The timeout ms.</param>
		void StopMonitoringProcessOutput(int timeoutMs = 2000);
	}

	/// <summary>Class IoOutputEventArgs.</summary>
	public class IoOutputEventArgs : EventArgs
	{
		/// <summary>Gets or sets a value indicating whether this instance is error.</summary>
		/// <value><c>true</c> if this instance is error; otherwise, <c>false</c>.</value>
		public bool IsError { get; set; }

		/// <summary>Gets or sets the text.</summary>
		/// <value>The text.</value>
		public string Text { get; set; }

		/// <summary>Initializes a new instance of the <see cref="IoOutputEventArgs"/> class.</summary>
		/// <param name="text">The text.</param>
		/// <param name="isError">if set to <c>true</c> [is error].</param>
		public IoOutputEventArgs(string text, bool isError = false)
		{
			Text = text;
			IsError = isError;
		}
	}
}

Open in new window

4. Build your solution. This will add the new control to the designer toolbox for this solution.

User generated image

5. Modify the code files for the projects existing form.

Form1.Designer.cs -
namespace EE_Q28794139
{
	partial class Form1
	{
		/// <summary>
		/// Required designer variable.
		/// </summary>
		private System.ComponentModel.IContainer components = null;

		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
		protected override void Dispose(bool disposing)
		{
			if (disposing && (components != null))
			{
				components.Dispose();
			}
			base.Dispose(disposing);
		}

		#region Windows Form Designer generated code

		/// <summary>
		/// Required method for Designer support - do not modify
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
			this.button1 = new System.Windows.Forms.Button();
			this.richTextBox1 = new CommandWindowRichTextBox();
			this.SuspendLayout();
			// 
			// button1
			// 
			this.button1.Location = new System.Drawing.Point(266, 379);
			this.button1.Name = "button1";
			this.button1.Size = new System.Drawing.Size(100, 23);
			this.button1.TabIndex = 0;
			this.button1.Text = "Start Backup";
			this.button1.UseVisualStyleBackColor = true;
			this.button1.Click += new System.EventHandler(this.OnClick);
			// 
			// richTextBox1
			// 
			this.richTextBox1.Location = new System.Drawing.Point(13, 13);
			this.richTextBox1.Name = "richTextBox1";
			this.richTextBox1.ReadOnly = true;
			this.richTextBox1.Size = new System.Drawing.Size(353, 360);
			this.richTextBox1.TabIndex = 1;
			this.richTextBox1.Text = "";
			// 
			// Form1
			// 
			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
			this.ClientSize = new System.Drawing.Size(378, 414);
			this.Controls.Add(this.richTextBox1);
			this.Controls.Add(this.button1);
			this.Name = "Form1";
			this.Text = "Form1";
			this.Load += new System.EventHandler(this.OnLoad);
			this.ResumeLayout(false);

		}

		#endregion

		private System.Windows.Forms.Button button1;
		private CommandWindowRichTextBox richTextBox1;
	}
}

Open in new window

Form1.cs -
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;

namespace EE_Q28794139
{
	public partial class Form1 : Form
	{
		/// <summary>The process field</summary>
		private Process fProcess = new Process();
		private string fProcessName = string.Empty;

		public Form1()
		{
			InitializeComponent();
		}

		private void OnClick(object sender, EventArgs e)
		{
			try
			{
				button1.Enabled = false;
				ProcessStartInfo info = new ProcessStartInfo()
				{
					Arguments = "\\USER-FOLDER \\SERVER\\DATA\\Home\\USER\\USERFOLDER /xo /s /Z /W:5",
					CreateNoWindow = true,
					ErrorDialog = false,
					FileName = "robocopy.exe",
					RedirectStandardError = true,
					RedirectStandardInput = true,
					RedirectStandardOutput = true,
					UseShellExecute = false,
					WorkingDirectory = "E:"
				};

				richTextBox1.HandleInvokeRequired(tb => tb.AppendText(string.Format("Running the command: {0}\r\n", info.FileName)));
				richTextBox1.HandleInvokeRequired(tb => tb.AppendText(string.Format("With the arguments: {0}\r\n", info.Arguments)));

				fProcess = Process.Start(info);
				fProcessName = fProcess.ProcessName;
				fProcess.EnableRaisingEvents = true;
				richTextBox1.ExecutingProcess = fProcess;
				richTextBox1.ProcessCompleted += OnProcessCompleted;
			}
			catch (Exception ex)
			{
				richTextBox1.HandleInvokeRequired(tb => tb.AppendText(string.Format("Exception reported in {0} - {1} [{2}]\r\n", ex.Source, ex, ex.Message)));
				button1.Enabled = true;
			}
		}

		private void OnProcessCompleted(object sender, EventArgs e)
		{
			if (!ProcessChecker.IsProcessRunning(fProcessName))
			{
				richTextBox1.HandleInvokeRequired(tb => tb.AppendText("Completed the backup process\r\n"));
				button1.Enabled = true;
			}
		}

		private void OnLoad(object sender, EventArgs e)
		{

		}
	}

	static class Extensions
	{
		public static void HandleInvokeRequired<T>(this T control, Action<T> action) where T : Control, ISynchronizeInvoke
		{
			//Check to see is the control is not null
			if (control == null)
				throw new ArgumentNullException(string.Format("Cannot execute {0} on {1}.  {1} is null.", action, control));

			//Check to see if the control is disposed.
			if (control is Control && (control as Control).IsDisposed)
				throw new ObjectDisposedException(string.Format("Cannot execute {0} on {1}.  {1} is disposed.", action, control));

			//Check to see if the handle is created for the control.
			if (control is Control && !(control as Control).IsHandleCreated)
				throw new InvalidOperationException(string.Format("Cannot execute {0} on {1}.  Handle is not created for {1}.", action, control));

			//Check to see if the control's InvokeRequired property is true
			if (control.InvokeRequired)
			{
				try
				{
					//Use Invoke() to invoke your action
					control.Invoke(action, new object[] { control });
				}
				catch (Exception ex)
				{
					throw new Exception(string.Format("Cannot execute {0} on {1}.  {2}.", action, control, ex.Message));
				}
			}
			else
			{
				try
				{
					//Perform the action
					action(control);
				}
				catch (Exception ex)
				{
					throw new Exception(string.Format("Cannot execute {0} on {1}.  {2}.", action, control, ex.Message));
				}
			}
		}
	}

	static class ProcessChecker
	{
		#region Variables
		/// <summary>Stores a required string that must be present in the window title for it to be detected.</summary>
		private static string fRequiredString;
		#endregion

		#region Private Methods
		/// <summary>Perform finding and showing of running window.</summary>
		/// <param name="hWnd">A handle to the window.</param>
		/// <param name="lParam">An application-defined value to be passed to the callback function.</param>
		/// <returns>Bool, which is important and must be kept to match up with system call.</returns>
		private static bool EnumerateWindowsProcesses(IntPtr hWnd, Int32 lParam)
		{
			int processId = 0;
			NativeMethods.GetWindowThreadProcessId(hWnd, ref processId);

			var caption = new StringBuilder(1024);
			NativeMethods.GetWindowText(hWnd, caption, 1024);

			// Use IndexOf to make sure our required string is in the title.
			if (processId == lParam && (caption.ToString().IndexOf(fRequiredString, StringComparison.OrdinalIgnoreCase) != -1))
			{
				// Restore the window.
				NativeMethods.ShowWindowAsync(hWnd, NativeMethods.SHOW_WINDOW.SW_SHOWNORMAL);
				NativeMethods.SetForegroundWindow(hWnd);
			}
			return true; // Keep this.
		}
		#endregion

		#region Public Methods
		/// <summary>Find out if we need to continue to load the current process. If we don't focus the old process that is equivalent to this one.</summary>
		/// <param name="forceTitle">This string must be contained in the window to restore. Use a string that contains the most unique sequence possible. If the program has windows with the string "Journal", pass that word.</param>
		/// <returns>False if no previous process was activated. True if we did focus a previous process and should simply exit the current one.</returns>
		public static bool IsOnlyProcess(string forceTitle)
		{
			fRequiredString = forceTitle;
			foreach (var proc in Process.GetProcessesByName(System.Windows.Forms.Application.ProductName))
			{
				if (proc.Id != Process.GetCurrentProcess().Id)
				{
					NativeMethods.EnumWindows(EnumerateWindowsProcesses, proc.Id);
					return false;
				}
			}
			return true;
		}

		/// <summary>Determines whether a process is running.</summary>
		/// <param name="processName">Name of the process.</param>
		/// <returns><c>true</c> if the process is running; otherwise, <c>false</c>.</returns>
		public static bool IsProcessRunning(string processName)
		{
			return Process.GetProcessesByName(processName).Length > 0 ? true : false;
		}

		/// <summary>Used to send messages to an application instance's message pump.</summary>
		/// <param name="format">String value to replace the current format item with.</param>
		/// <param name="args">Object to format with the preceding string value.</param>
		/// <returns>Returns a unique message that can be sent through the application instances message pump.</returns>
		public static int RegisterWindowMessage(string format, params object[] args)
		{
			string message = string.Format(format, args);
			return NativeMethods.RegisterWindowMessage(message);
		}

		/// <summary>Method that attempts to bring the application instances active window to the foreground of the desktop.</summary>
		/// <param name="window">Integer value that represents the window handle of the application instance to bring to the front.</param>
		public static void ShowToFront(IntPtr window)
		{
			NativeMethods.ShowWindow(window, NativeMethods.SHOW_WINDOW.SW_SHOWNORMAL);
			NativeMethods.SetForegroundWindow(window);
		}
		#endregion
	}

	/// <summary>Delegate method to enumerate windows processes.</summary>
	public delegate bool EnumerateWindowsProcessesDelegate(IntPtr hWnd, Int32 lParam);

	static class NativeMethods
	{
		/// <summary>The SHOW_WINDOW enumeration contains values that specify the window states.</summary>
		public enum SHOW_WINDOW : int
		{
			SW_HIDE = 0,
			SW_SHOWNORMAL = 1,
			SW_NORMAL = 1,
			SW_SHOWMINIMIZED = 2,
			SW_SHOWMAXIMIZED = 3,
			SW_MAXIMIZE = 3,
			SW_SHOWNOACTIVATE = 4,
			SW_SHOW = 5,
			SW_MINIMIZE = 6,
			SW_SHOWMINNOACTIVE = 7,
			SW_SHOWNA = 8,
			SW_RESTORE = 9,
			SW_SHOWDEFAULT = 10,
			SW_FORCEMINIMIZE = 11,
			SW_MAX = 11
		}

		/// <summary>Enumerates all top-level windows on the screen by passing the handle to each window, in turn, to an application-defined callback function. EnumWindows continues until the last top-level window is 
		/// enumerated or the callback function returns FALSE.</summary>
		/// <param name="lpEnumFunc">A pointer to an application-defined callback function. For more information, see EnumWindowsProc.</param>
		/// <param name="lParam">An application-defined value to be passed to the callback function.</param>
		/// <returns>If the function succeeds, the return value is nonzero.  
		/// If the function fails, the return value is zero. To get extended error information, call GetLastError.  
		/// If EnumWindowsProc returns zero, the return value is also zero. In this case, the callback function should call SetLastError to obtain a meaningful error code to be returned to the caller of EnumWindows.
		/// </returns>
		/// <remarks>The EnumWindows function does not enumerate child windows, with the exception of a few top-level windows owned by the system that have the WS_CHILD style.  This function is more reliable than 
		/// calling the GetWindow function in a loop. An application that calls GetWindow to perform this task risks being caught in an infinite loop or referencing a handle to a window that has been destroyed.
		/// </remarks>
		[DllImport("user32.dll", SetLastError = true)]
		public static extern bool EnumWindows(EnumerateWindowsProcessesDelegate lpEnumFunc, Int32 lParam);

		/// <summary>Copies the text of the specified window's title bar (if it has one) into a buffer. If the specified window is a control, the text of the control is copied. However, GetWindowText cannot retrieve 
		/// the text of a control in another application.</summary>
		/// <param name="hWnd">A handle to the window or control containing the text.</param>
		/// <param name="lpString">The buffer that will receive the text.  If the string is as long or longer than the buffer, the string is truncated and terminated with a null character.</param>
		/// <param name="nMaxCount">The maximum number of characters to copy to the buffer, including the null character. If the text exceeds this limit, it is truncated.</param>
		/// <returns>If the function succeeds, the return value is the length, in characters, of the copied string, not including the terminating null character.  If the window has no title bar or text, if the title 
		/// bar is empty, or if the window or control handle is invalid, the return value is zero. To get extended error information, call GetLastError.  This function cannot retrieve the text of an edit control in 
		/// another application.</returns>
		/// <remarks>If the target window is owned by the current process, GetWindowText causes a WM_GETTEXT message to be sent to the specified window or control. If the target window is owned by another process and 
		/// has a caption, GetWindowText retrieves the window caption text. If the window does not have a caption, the return value is a null string. This behavior is by design. It allows applications to call 
		/// GetWindowText without becoming unresponsive if the process that owns the target window is not responding. However, if the target window is not responding and it belongs to the calling application, 
		/// GetWindowText will cause the calling application to become unresponsive.  To retrieve the text of a control in another process, send a WM_GETTEXT message directly instead of calling GetWindowText.</remarks>
		[DllImport("user32.dll", SetLastError = true)]
		public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, Int32 nMaxCount);

		/// <summary>Retrieves the identifier of the thread that created the specified window and, optionally, the identifier of the process that created the window.</summary>
		/// <param name="hWnd">A handle to the window.</param>
		/// <param name="lpdwProcessId">A pointer to a variable that receives the process identifier. If this parameter is not NULL, GetWindowThreadProcessId copies the identifier of the process to the variable; 
		/// otherwise, it does not.</param>
		/// <returns>The return value is the identifier of the thread that created the window.</returns>
		[DllImport("user32.dll", SetLastError = true)]
		public static extern int GetWindowThreadProcessId(IntPtr hWnd, ref Int32 lpdwProcessId);

		/// <summary>Defines a new window message that is guaranteed to be unique throughout the system. The message value can be used when sending or posting messages.</summary>
		/// <param name="lpString">The message to be registered.</param>
		/// <returns>If the message is successfully registered, the return value is a message identifier in the range 0xC000 through 0xFFFF.  If the function fails, the return value is zero. To get extended error 
		/// information, call GetLastError.</returns>
		/// <remarks>The RegisterWindowMessage function is typically used to register messages for communicating between two cooperating applications.  If two different applications register the same message string, 
		/// the applications return the same message value. The message remains registered until the session ends.  Only use RegisterWindowMessage when more than one application must process the same message. For 
		/// sending private messages within a window class, an application can use any integer in the range WM_USER through 0x7FFF. (Messages in this range are private to a window class, not to an application. For 
		/// example, predefined control classes such as BUTTON, EDIT, LISTBOX, and COMBOBOX may use values in this range.)</remarks>
		[DllImport("user32", SetLastError = true)]
		public static extern int RegisterWindowMessage(string lpString);

		/// <summary>Brings the thread that created the specified window into the foreground and activates the window. Keyboard input is directed to the window, and various visual cues are changed for the user. The 
		/// system assigns a slightly higher priority to the thread that created the foreground window than it does to other threads.</summary>
		/// <param name="handle">A handle to the window that should be activated and brought to the foreground.</param>
		/// <returns>If the window was brought to the foreground, the return value is nonzero.  If the window was not brought to the foreground, the return value is zero.</returns>
		/// <remarks>The system restricts which processes can set the foreground window. A process can set the foreground window only if one of the following conditions is true:  
		/// •The process is the foreground process.  
		/// •The process was started by the foreground process.  
		/// •The process received the last input event.  
		/// •There is no foreground process.  
		/// •The foreground process is being debugged.  
		/// •The foreground is not locked (see LockSetForegroundWindow).  
		/// •The foreground lock time-out has expired (see SPI_GETFOREGROUNDLOCKTIMEOUT in SystemParametersInfo).  
		/// •No menus are active.  
		/// An application cannot force a window to the foreground while the user is working with another window. Instead, Windows flashes the taskbar button of the window to notify the user.  A process that can set 
		/// the foreground window can enable another process to set the foreground window by calling the AllowSetForegroundWindow function. The process specified by dwProcessId loses the ability to set the foreground 
		/// window the next time the user generates input, unless the input is directed at that process, or the next time a process calls AllowSetForegroundWindow, unless that process is specified.  The foreground 
		/// process can disable calls to SetForegroundWindow by calling the LockSetForegroundWindow function.</remarks>
		[DllImport("user32.dll", EntryPoint = "SetForegroundWindow", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, SetLastError = true)]
		public static extern bool SetForegroundWindow(IntPtr handle);

		/// <summary>Sets the specified window's show state.</summary>
		/// <param name="handle">A handle to the window.</param>
		/// <param name="nCmd">Controls how the window is to be shown.  This parameter is ignored the first time an application calls ShowWindow, if the program that launched the application provides a STARTUPINFO 
		/// structure.  Otherwise, the first time ShowWindow is called, the value should be the value obtained by the WinMain function in its nCmdShow parameter.  In subsequent calls, this parameter can be one of the 
		/// following values:  
		/// SW_FORCEMINIMIZE (11) - Minimizes a window, even if the thread that owns the window is not responding. This flag should only be used when minimizing windows from a different thread.  
		/// SW_HIDE (0) - Hides the window and activates another window.  
		/// SW_MAXIMIZE (3) - Maximizes the specified window.  
		/// SW_MINIMIZE (6) - Minimizes the specified window and activates the next top-level window in the Z order.  
		/// SW_RESTORE (9) - Activates and displays the window. If the window is minimized or maximized, the system restores it to its original size and position. An application should specify this flag when 
		/// restoring a minimized window.  
		/// SW_SHOW (5) - Activates the window and displays it in its current size and position.  
		/// SW_SHOWDEFAULT (10) - Sets the show state based on the SW_ value specified in the STARTUPINFO structure passed to the CreateProcess function by the program that started the application.  
		/// SW_SHOWMAXIMIZED (3) - Activates the window and displays it as a maximized window.  
		/// SW_SHOWMINIMIZED (2) - Activates the window and displays it as a minimized window.  
		/// SW_SHOWMINNOACTIVE (7) - Displays the window as a minimized window. This value is similar to SW_SHOWMINIMIZED, except the window is not activated.  
		/// SW_SHOWNA (8) - Displays the window in its current size and position. This value is similar to SW_SHOW, except that the window is not activated.  
		/// SW_SHOWNOACTIVATE (4) - Displays a window in its most recent size and position. This value is similar to SW_SHOWNORMAL, except that the window is not activated.  
		/// SW_SHOWNORMAL (1) - Activates and displays a window. If the window is minimized or maximized, the system restores it to its original size and position. An application should specify this flag when 
		/// displaying the window for the first time.</param>
		/// <returns>If the window was previously visible, the return value is nonzero.  If the window was previously hidden, the return value is zero</returns>
		/// <remarks>To perform certain special effects when showing or hiding a window, use AnimateWindow.  The first time an application calls ShowWindow, it should use the WinMain function's nCmdShow parameter as 
		/// its nCmdShow parameter. Subsequent calls to ShowWindow must use one of the values in the given list, instead of the one specified by the WinMain function's nCmdShow parameter.  As noted in the discussion 
		/// of the nCmdShow parameter, the nCmdShow value is ignored in the first call to ShowWindow if the program that launched the application specifies startup information in the structure. In this case, 
		/// ShowWindow uses the information specified in the STARTUPINFO structure to show the window. On subsequent calls, the application must call ShowWindow with nCmdShow set to SW_SHOWDEFAULT to use the startup 
		/// information provided by the program that launched the application. This behavior is designed for the following situations:  
		/// •Applications create their main window by calling CreateWindow with the WS_VISIBLE flag set.  
		/// •Applications create their main window by calling CreateWindow with the WS_VISIBLE flag cleared, and later call ShowWindow with the SW_SHOW flag set to make it visible.</remarks>
		[DllImport("user32.dll", EntryPoint = "ShowWindow", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, SetLastError = true)]
		[return: MarshalAs(UnmanagedType.Bool)]
		public static extern bool ShowWindow(IntPtr handle, SHOW_WINDOW nCmd);

		/// <summary>Sets the show state of a window created by a different thread.</summary>
		/// <param name="hWnd">A handle to the window.</param>
		/// <param name="nCmdShow">Controls how the window is to be shown. For a list of possible values, see the description of the ShowWindow function.</param>
		/// <returns>If the window was previously visible, the return value is nonzero.  If the window was previously hidden, the return value is zero.</returns>
		/// <remarks>This function posts a show-window event to the message queue of the given window. An application can use this function to avoid becoming nonresponsive while waiting for a nonresponsive 
		/// application to finish processing a show-window event.</remarks>
		[DllImport("user32.dll", SetLastError = true)]
		public static extern bool ShowWindowAsync(IntPtr hWnd, SHOW_WINDOW nCmd);

	}
}

Open in new window


Once completed the designer for Form1 should look similar to:User generated imageBuild and run the solution.  Click the start backup button.  You should get any reported errors in the command box of the form; e.g. -User generated image
This will help us sort out any errors in your command.

-saige-
Avatar of jlspencer
jlspencer

ASKER

I placed a breakpoint at "WorkingDirectory line and stepped through the startInfo lines, easy enough but I have no idea how or what to copy  I'm still very green at this. There is not a CMD windows to see any results of the robocopy process.
If you hover over the process info object you can start drilling into it and pseudo select the data from  given parameters. Not at my desk so ill have to send picks later if you cant get it
Follow the instructions as I have laid out so that we can get a command window to see the results.  For example, when I changed my working directory and arguments so that they would match a configuration on my computer; e.g. -
private void OnClick(object sender, EventArgs e)
{
	try
	{
		button1.Enabled = false;
		ProcessStartInfo info = new ProcessStartInfo()
		{
			Arguments = "C:\\!quick\\Test F:\\!quick\\Test /xo /s /Z /W:5",
			CreateNoWindow = true,
			ErrorDialog = false,
			FileName = "robocopy.exe",
			RedirectStandardError = true,
			RedirectStandardInput = true,
			RedirectStandardOutput = true,
			UseShellExecute = false,
			WorkingDirectory = "F:"
		};

		richTextBox1.HandleInvokeRequired(tb => tb.AppendText(string.Format("Running the command: {0}\r\n", info.FileName)));
		richTextBox1.HandleInvokeRequired(tb => tb.AppendText(string.Format("With the arguments: {0}\r\n", info.Arguments)));

		fProcess = Process.Start(info);
		fProcessName = fProcess.ProcessName;
		fProcess.EnableRaisingEvents = true;
		richTextBox1.ExecutingProcess = fProcess;
		richTextBox1.ProcessCompleted += OnProcessCompleted;
	}
	catch (Exception ex)
	{
		richTextBox1.HandleInvokeRequired(tb => tb.AppendText(string.Format("Exception reported in {0} - {1} [{2}]\r\n", ex.Source, ex, ex.Message)));
		button1.Enabled = true;
	}
}

Open in new window

I got the following output -User generated imageUser generated image
-saige-
User generated image
You can click in this area and copy the text.
Well I hovered and drilled down several layers, not sure what I should be looking for but so far I have not struck any oil.
Arguments- we want to copy them
Add supplied code and then we should get your robocopy string in notepad.

startInfo.FileName = "robocopy.exe";
startInfo.Arguments = "\"\\USER-FOLDER \\SERVER\\DATA\\Home\\USER\\USERFOLDER /xo /s /Z /W:5";

                    //---| Add the code below |---
                      StringBuilder sb = new StringBuilder();
                    sb.Append( psi.FileName + " " + psi.Arguments );

                    string filename = Path.GetTempFileName();

                    File.WriteAllText( filename , sb.ToString() );

                    Process p = new Process();
                    ProcessStartInfo psi = new ProcessStartInfo();

                       psi.Arguments = filename;

                    psi.FileName = "Notepad.exe";

                    p.StartInfo = psi;
                    p.EnableRaisingEvents = true;
                    p.Start();
                    //---| Add the code above |---

Open in new window

Saige,  
I'm sure your code is outstanding, however it is so beyond my understanding right now. I tried a copy / paste and that did not work. and I do not have time to type out 900+ lines of code.
William,

Progress, it appears from this that the source  / destination   is the issue.

==============================================================


-------------------------------------------------------------------------------
   ROBOCOPY     ::     Robust File Copy for Windows                              
-------------------------------------------------------------------------------

  Started : Friday, October 30, 2015 2:31:43 PM
   Source = E:\2016_BP_TIU \rmf-tld-fs01\rmf-data\Home\jleilerman\Projects\2016_BP_TIU \xo \s \Z \W:5\
     Dest -

    Files : *.*
          
  Options : *.* /DCOPY:DA /COPY:DAT /R:1000000 /W:30

------------------------------------------------------------------------------

ERROR : No Destination Directory Specified.

       Simple Usage :: ROBOCOPY source destination /MIR

             source :: Source Directory (drive:\path or \\server\share\path).
        destination :: Destination Dir  (drive:\path or \\server\share\path).
               /MIR :: Mirror a complete directory tree.

    For more usage information run ROBOCOPY /?

                                                         
****  /MIR can DELETE files as well as copy them !
jlspencer -
Looks like dest missing.
You may want to wrap the source and dest in quotes as well - I do this always as a precaution.
Also is one of your destination's [startInfo.WorkingDirectory = "E:";]?
You are missing a leading '\' on the destination portion of your arguments:

"E:\2016_BP_TIU \rmf-tld-fs01\rmf-data\Home\jleilerman\Projects\2016_BP_TIU \xo \s \Z"

Should be:

"E:\2016_BP_TIU \\rmf-tld-fs01\rmf-data\Home\jleilerman\Projects\2016_BP_TIU \xo \s \Z"

What this translates to in code is:
 "\"\\USER-FOLDER \\\\SERVER\\DATA\\Home\\USER\\USERFOLDER /xo /s /Z /W:5";

Open in new window

-saige-
I tried that and had the following errors. I also tried various other combinations.

User generated image
Just found the  extra "  now lets try this again.
That made no difference:
User generated image
jlspencer - You need add the slashes as it_saige has shown previously:
 "\"\\USER-FOLDER \\\\SERVER\\DATA\\Home\\USER\\USERFOLDER /xo /s /Z /W:5";

Open in new window


This will make the single source into a source and destination - A UNC path has to start like "\\SERVER\SHARE" It can not start like "\SERVER\SHARE"
Not sure why but when I capture this it is removing the leading  \ off of the destination  this is the result:

Source = e:\folder  \server name\data\home\user\folder
Dest -

Here is the code for source and destination,.

                startInfo.FileName = "robocopy.exe";
                startInfo.Arguments = "\"e:\\folder  \\\\servername\\data\\Home\\user\\folder /xo /s /Z /W:5";
ASKER CERTIFIED SOLUTION
Avatar of William Domenz
William Domenz
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
William I also added another label :

   BUResultslabel.Text = output;

Now the results of the process is displayed to the user.
Nice jlspencer - Glad you got this running!