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 input from a stream to which the user input is to be sent.
Full story: I'm building a Cmd emulator and at first everything looked fine: The user enters a command, it gets echoed to the output area, processed and StdOut and StdErrOut are captured and also added to the output TextBox.
The only problem was, that, as the cmd process was created and started separately for each command, no state was kept. Neither variables nor codepage nor working directory etc..
So I decided to invent a little hack: Entering an opening or closing parenthesis starts and stops collecting the commands instead of executing them. After the closing parenthesis the list of commands ('batch') is used in the processBatch method to feed them all to the cmd process vie its redirected input. Worked fine.
The only problem was, obviously, now I got state but lost immediate response, so any errors wouldn't pop up until the batch was run.
So I decided to combine the good parts and, well, I knew I was heading for trouble when I realized, that to keep two loops working & waiting I have to use threading. Which I haven't done in years..
In the layout I chose the main() loop waits for user input and startCMDtask() runs startCMD() in a task. Here the input stream is scanned until is has data and then the cmd process is to process them..
But it doesn't work.
List<string> batch = new List<string>();
public volatile string output = "+";
public volatile string outputErr = "-";
Process CMD;
Task cmdTask;
volatile Queue<string> cmdQueue = new Queue<string>();
volatile public bool CMDrunning = false;
Tthis works just fine
private void processBatch()
{
Process p = new Process();
ProcessStartInfo info = new ProcessStartInfo();
info.FileName = "cmd.exe";
info.RedirectStandardOutput = true;
info.RedirectStandardError = true;
info.RedirectStandardInput = true;
info.UseShellExecute = false;
p.StartInfo = info;
p.Start();
using (StreamWriter sw = p.StandardInput)
{
if (sw.BaseStream.CanWrite)
foreach(string line in batch) sw.WriteLine(line);
}
output = "^"; outputErr = "~";
try { output = p.StandardOutput.ReadToEnd(); } catch { }
try { outputErr = p.StandardError.ReadToEnd(); } catch { }
try { p.WaitForExit(); } catch { }
tb_output.AppendText(output + "\r\n" + outputErr + "\r\n");
}
These don't quite, but almost..
private void setupCMD()
{
CMD = new Process();
ProcessStartInfo info = new ProcessStartInfo();
info.FileName = "cmd.exe";
// info.Arguments = "/K"; // doesn't make a difference
info.CreateNoWindow = true;
info.RedirectStandardOutput = true;
info.RedirectStandardError = true;
info.RedirectStandardInput = true;
info.UseShellExecute = false;
CMD.StartInfo = info;
}
private void startCMDtask()
{
var task = Task.Factory.StartNew(() => startCMD());
cmdTask = task;
}
private void startCMD()
{
try { CMD.Start(); CMDrunning = true; }
catch { output = "Error starting cmd process.\r\n"; CMDrunning = false; }
using (StreamWriter sw = CMD.StandardInput)
{
if (sw.BaseStream.CanWrite)
do {
try
{
string cmd = cmdQueue.Dequeue();
if (cmd != null & cmd !="")
{
sw.WriteLine(cmd);
processOutputStreams();
}
}
catch {}
} while (CMDrunning);
}
private void processOutputStreams()
{
string newOutput = ""; string newOutputErr = "";
while (CMD.StandardOutput.Peek() > 0)
newOutput += (char)(CMD.StandardOutput.Read());
newOutput += "!?"; // at this point stdout is correctly captured (1)
try {
while (CMD.StandardError.Peek() > 0) // from here execution jumps away (2)
{ newOutputErr += (char)(CMD.StandardError.Read()); }
} catch {
newOutputErr = "?"; // never comes here
}
lock (output) // no noticable difference
lock (outputErr) //
{ // if I jump here (3) from (1) the result is displayed
// but not if i comment out the 2nd while loop (2)
if (newOutput != null & newOutput != "") output += newOutput + "\r\n";
if (newOutputErr != null & newOutputErr != "") outputErr += newOutputErr + "\r\n";
}
}
This is the call from the input processor in the main thread:
lock (cmdQueue) cmdQueue.Enqueue(cmd);
I have no idea which part is the problem: the process, the cmd shell, the input stream, the output stream, the threading, the locks or all of it in turns..??