【发布时间】:2014-04-12 21:56:17
【问题描述】:
这个问题听起来有点密集。这是一个稍长的版本:
我需要让主循环等待用户输入,并且还有一个进程正在运行并等待用户输入要发送到的流中的输入。
完整故事:我正在构建一个 Cmd 模拟器,起初一切看起来都很好:用户输入命令,它被回显到输出区域,经过处理,StdOut 和 StdErrOut 被捕获并添加到输出 TextBox。
唯一的问题是,由于 cmd 进程是为每个命令单独创建和启动的,因此没有保留任何状态。既不是变量,也不是代码页,也不是工作目录等。
所以我决定发明一个小技巧:输入左括号或右括号开始和停止收集命令,而不是执行它们。在右括号之后,在 processBatch 方法中使用命令列表('batch'),通过其重定向输入将它们全部提供给 cmd 进程。工作得很好。
显然,唯一的问题是,现在我得到了状态但失去了立即响应,所以在批处理运行之前不会弹出任何错误。
所以我决定把好的部分结合起来,好吧,当我意识到要保持两个循环工作和等待时,我知道我正面临麻烦,我必须使用线程。我已经很多年没有做过了..
在布局中,我选择了 main() 循环等待用户输入,而 startCMDtask() 在任务中运行 startCMD()。这里扫描输入流,直到有数据,然后cmd进程处理它们..
但它不起作用。
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;
这很好用
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");
}
这些不完全,但几乎..
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";
}
}
这是来自主线程中输入处理器的调用:
lock (cmdQueue) cmdQueue.Enqueue(cmd);
我不知道是哪一部分出了问题:进程、cmd shell、输入流、输出流、线程、锁或所有这些轮流......??
【问题讨论】:
-
感谢连续投反对票!
标签: c# process cmd task streamwriter