The question sounds a bit, dense. Here is a slightly longer version:
I need to have the main loop wait for user input and also have a process running and waiting for inp
I finally got it working. The reason for the erratic behaviour I described in the code samples was that the 3 streams were not accessed in an async'ed manner.
To rectify I discarded the processOutput function and replaced it by two calls that the process itself triggers. MS documetation gives a fine example here
I also made the StreamWriter sync, that feeds the process and the whole task it runs in as well.
Here is the new code:
private void startCMDtask()
{
var task = Task.Factory.StartNew(() => startCMD());
cmdTask = task;
}
private async void startCMD()
{
try { CMD.Start(); CMDrunning = true; }
catch { cmdErrOutput.Append("\r\nError starting cmd process.");
CMDrunning = false; }
CMD.BeginOutputReadLine();
CMD.BeginErrorReadLine();
using (StreamWriter sw = CMD.StandardInput)
{
if (sw.BaseStream.CanWrite)
do {
try
{
string cmd = cmdQueue.Dequeue();
if (cmd != null & cmd !="") await sw.WriteLineAsync(cmd);
}
catch { }
} while (CMDrunning);
try { CMD.WaitForExit(); }
catch { cmdErrOutput.Append("WaitForExit Error.\r\n"); }
}
}
This is how the process is set up now:
private void setupCMD()
{
CMD = new Process();
ProcessStartInfo info = new ProcessStartInfo();
info.FileName = "cmd.exe";
info.CreateNoWindow = true;
info.RedirectStandardOutput = true;
info.RedirectStandardError = true;
info.RedirectStandardInput = true;
info.UseShellExecute = false;
CMD.OutputDataReceived += new DataReceivedEventHandler(cmdOutputDataHandler);
CMD.ErrorDataReceived += new DataReceivedEventHandler(cmdErrorDataHandler);
cmdOutput = new StringBuilder();
cmdErrOutput = new StringBuilder();
CMD.StartInfo = info;
}
And here are the output handlers:
private static void cmdOutputDataHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
if (!String.IsNullOrEmpty(outLine.Data))
{ // Add the text to the collected output.
cmdOutput.Append(Environment.NewLine + outLine.Data);
}
}
private static void cmdErrorDataHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
if (!String.IsNullOrEmpty(outLine.Data))
{ // Add the text to the collected error output.
cmdErrOutput.Append(Environment.NewLine + outLine.Data);
}
}
At the end of the user input porcessing this is how the input queue is ged and the output fetched:
cmdUnDoStack.Push(cmd);
Application.DoEvents();
TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(() => updateOutputArea(uiScheduler));
Using this little routine:
private void updateOutputArea(TaskScheduler uiScheduler)
{
Task.Factory.StartNew(() =>
{
tb_output.AppendText(cmdOutput + "\r\n" + cmdErrOutput + "\r\n");
cmdOutput.Clear();
cmdErrOutput.Clear();
}, System.Threading.CancellationToken.None, TaskCreationOptions.None, uiScheduler);
}
And now for the special treament some of the good old commands like CLS or COLOR need.. ;-)