转自:http://blog.csdn.net/vodistr03/article/details/6658340
一、同步方式
这里有一个简单编写的小例子,是一个win窗口程序。为点击按钮,在后台启动控制台,按照程序设定输入命令,并将返回的输出打印在文本框中,下面是核心代码——按钮点击的响应函数。这里存在着有诸多问题:
- private void button1_Click(object sender, EventArgs e)
- {
- ProcessStartInfo start = new ProcessStartInfo("cmd.exe");
- start.CreateNoWindow = true; //不显示dos命令行窗口
- start.RedirectStandardOutput = true;
- start.RedirectStandardInput = true;
- start.RedirectStandardError = true;
- start.UseShellExecute = false; //是否指定操作系统外壳进程启动程序,这里需为false
- Process p = Process.Start(start);
- StreamWriter writer = p.StandardInput;
- writer.WriteLine("ping www.baidu.com");
- writer.WriteLine("exit");
- StreamReader reader = p.StandardOutput; //截取输出流
- StreamReader readerErr = p.StandardError; //截取错误流
- string line = reader.ReadLine();
- while (!reader.EndOfStream)
- {
- tbResult.AppendText(line+"\r\n");
- line = reader.ReadLine();
- }
- string lineErr = readerErr.ReadLine();
- while (!reader.EndOfStream)
- {
- tbResult.AppendText(lineErr + "\r\n");
- lineErr = readerErr.ReadLine();
- }
- p.WaitForExit(); //等待程序执行完退出进程
- p.Close(); //关闭进程
- reader.Close(); //关闭流
- readerErr.Close();
- }
private void button1_Click(object sender, EventArgs e) { ProcessStartInfo start = new ProcessStartInfo("cmd.exe"); start.CreateNoWindow = true; //不显示dos命令行窗口 start.RedirectStandardOutput = true; start.RedirectStandardInput = true; start.RedirectStandardError = true; start.UseShellExecute = false; //是否指定操作系统外壳进程启动程序,这里需为false Process p = Process.Start(start); StreamWriter writer = p.StandardInput; writer.WriteLine("ping www.baidu.com"); writer.WriteLine("exit"); StreamReader reader = p.StandardOutput; //截取输出流 StreamReader readerErr = p.StandardError; //截取错误流 string line = reader.ReadLine(); while (!reader.EndOfStream) { tbResult.AppendText(line+"\r\n"); line = reader.ReadLine(); } string lineErr = readerErr.ReadLine(); while (!reader.EndOfStream) { tbResult.AppendText(lineErr + "\r\n"); lineErr = readerErr.ReadLine(); } p.WaitForExit(); //等待程序执行完退出进程 p.Close(); //关闭进程 reader.Close(); //关闭流 readerErr.Close(); }
(1)子后台控制台执行命令开始执行至退出,主窗口会不响应用户操作,也就是通常说的阻塞,这可以通过多线程来解决。另建一线程在后台执行,调用进程启动函数,使之不影响主线程。
(2)程序中这种做法的硬伤是极有可能造成错误流输出的异常,当然在本程序中由于不存在错误流,故异常体现不出来。如果执行的是makefile编译的make命令,既有输出流又有错误流,如果想要通过这种方法在界面上同时显示输出和错误信息,是极不靠谱的,容易发生死锁。这个死锁缺陷也是同步读取重定向流的硬伤,如果一定要同时重定向错误流和输出流,最好使用异步读取重定向方式。采用异步的同时,也应采用多线程,因此对控件的操作要依照多线程的规范。下面是msdn的引述:
同步读取操作在读取StandardOutput 流的调用方及写入该流中的子进程之间引入一个依赖项。这些依赖项可能导致产生死锁情况。调用方读取子进程的重定向流时依赖于该子进程。调用方将等待读取操作,直到子进程写入流或关闭流为止。子进程写入足够多的数据以填充重定向流的时间依赖于父进程。子进程将等待下一次写操作,直到父进程读取了全部流或关闭该流为止。当调用方和子进程相互等待对方完成操作时,就会产生死锁情况,使双方都无法继续执行操作。您可以通过计算调用方和子进程之间的依赖项从而避免出现死锁情况。
二、多线程下的异步读取
- private void button1_Click(object sender, EventArgs e)
- {
- Thread th = new Thread(exeFile);
- th.IsBackground = true;
- th.Start();
- }
- private void exeFile()
- {
- ProcessStartInfo start = new ProcessStartInfo("cmd.exe");
- start.CreateNoWindow = true; //不显示dos命令行窗口
- start.RedirectStandardOutput = true;
- start.RedirectStandardInput = true;
- start.RedirectStandardError = true;
- start.UseShellExecute = false; //是否指定操作系统外壳进程启动程序,这里需为false
- Process p = Process.Start(start);
- StreamWriter writer = p.StandardInput;
- writer.WriteLine("ping www.baidu.com");
- writer.WriteLine("exit");
- writer.Close();
- p.OutputDataReceived += new DataReceivedEventHandler(p_OutputDataReceived);
- p.BeginOutputReadLine();
- p.WaitForExit(); //等待程序执行完退出进程
- p.Close(); //关闭进程
- }
- void p_OutputDataReceived(object sender, DataReceivedEventArgs e)
- {
- if (!string.IsNullOrEmpty(e.Data))
- {
- showText(tbResult, e.Data);
- }
- }
- private delegate void showTextDelegate(TextBox tb, string line);
- private void showText(TextBox tb, string line)
- {
- if (tb.InvokeRequired)
- {
- showTextDelegate d = showText;
- object[] a = { tb, line };
- tb.Invoke(d, a);
- }
- else
- {
- tb.AppendText(line + "\r\n");
- }
- }
private void button1_Click(object sender, EventArgs e) { Thread th = new Thread(exeFile); th.IsBackground = true; th.Start(); } private void exeFile() { ProcessStartInfo start = new ProcessStartInfo("cmd.exe"); start.CreateNoWindow = true; //不显示dos命令行窗口 start.RedirectStandardOutput = true; start.RedirectStandardInput = true; start.RedirectStandardError = true; start.UseShellExecute = false; //是否指定操作系统外壳进程启动程序,这里需为false Process p = Process.Start(start); StreamWriter writer = p.StandardInput; writer.WriteLine("ping www.baidu.com"); writer.WriteLine("exit"); writer.Close(); p.OutputDataReceived += new DataReceivedEventHandler(p_OutputDataReceived); p.BeginOutputReadLine(); p.WaitForExit(); //等待程序执行完退出进程 p.Close(); //关闭进程 } void p_OutputDataReceived(object sender, DataReceivedEventArgs e) { if (!string.IsNullOrEmpty(e.Data)) { showText(tbResult, e.Data); } } private delegate void showTextDelegate(TextBox tb, string line); private void showText(TextBox tb, string line) { if (tb.InvokeRequired) { showTextDelegate d = showText; object[] a = { tb, line }; tb.Invoke(d, a); } else { tb.AppendText(line + "\r\n"); } }
三、其他
注意telnet.exe并不支持流的重定向。