使用Process类重定向输出与错误时遇到的问题 (转)
程序中要调用外部程序cmd.exe执行一些命令行,并取得屏幕输出,使用了Process类,基本代码如下:
Process process = new Process(); process.StartInfo.FileName = "cmd.exe"; process.StartInfo.UseShellExecute = false; process.StartInfo.RedirectStandardInput = true; process.StartInfo.RedirectStandardOutput = true; process.StartInfo.RedirectStandardError = true; process.StartInfo.CreateNoWindow = true; process.Start();
………… //一些处理
process.StandardInput.WriteLine("exit"); //取输出内容 String outputString = process.StandardOutput.ReadToEnd(); process.WaitForExit(); process.Close();
实际使用中发现有时程序会无响应,并且是在执行某些特定的命令时才会无响应。跟踪调试发现,问题出在process.StandardOutput.ReadToEnd()上,在这一行按F10,程序不向下执行,也不产生Exception。
[解决过程]
由于调试时程序不向下执行,猜测ReadToEnd()没有返回,被阻塞住了,查了查MSDN,没有看到有相关内容。又反编了一下mscorlib.dll,看了看StreamReader.ReadToEnd()的实现方法,是这么一段代码:
public override string ReadToEnd() { int num1; if (this.stream == null) { __Error.ReaderClosed(); } char[] chArray1 = new char[this.charBuffer.Length]; StringBuilder builder1 = new StringBuilder(this.charBuffer.Length); while ((num1 = this.Read(chArray1, 0, chArray1.Length)) != 0) { builder1.Append(chArray1, 0, num1); } return builder1.ToString(); }
从中看出ReadToEnd()是利用Read()方法实现的,又去MSDN看了,仍然没有提到Read()是阻塞函数,于是只能上网去查一查相关资料,有人提到了类似的问题,并且给出了解决方法:使用两个线程分别做StandardOutput.ReadToEnd()和StandardError.ReadToEnd()。试了一下,果然可以解决。
[后续思考]
问题虽然解决,不过原理没有弄明白,还要继续深入。查阅相关资料,大致了解了Process的运作模式,它以子进程的形式启动cmd.exe,如果指定了重定向Input、Output或Error,就建立相应的管道在父子进程之间进行通讯。
基于这些内容以及关于管道的理解,得到以下几条猜测:
1、input、output、error的三条管道是相互独立的。
2、管道有大小,空时不可读,满时不可写。
3、遇到管道不可读写时,相应的进程会阻塞等待可读写为止。
4、子进程结束前,output流与error流不会结束。
这样再翻回来考虑之前遇到的问题,可以猜测无响应时是这样一种情况:
建立Process时指定了Output和Error都重定向,父子进程间就建立了2条管道,cmd.exe不停输出output与error,分别进入两条管道,对output,管道满后,子进程会停下来等待父进程取走数据,父进程的StandardOutput.ReadToEnd()方法正是取数据的,它在管道空时会处于阻塞状态,有数据时就取走,这样子进程会继续写output。
但是对于error,父进程没有相应的ReadToEnd()方法,很快error的管道就满了,由于无法写error,子进程会停下来等待,但是父进程永远不读,子进程也就永远不再继续,形成死锁。
对于不出问题的情况,一定是error管道根本就没有写满,这也就解释了为什么命令的内容决定了是否会出现无响应的情况。
那么直接在StandardOutput.ReadToEnd()后面加一条StandardError.ReadToEnd()行不行呢?答案是不行的,基于上面的猜测与分析,ReadToEnd()方法是阻塞的,在子进程结束前,output流不会关闭,所以StandardOutput.ReadToEnd()会一直等待,造成后面的StandardError.ReadToEnd()方法根本不被执行,还是会产生死锁。所以解决方案里才提到了要建立2个线程,让两个管道的ReadToEnd()方法独立执行。
再回来考虑我的程序中的情况,之所以没有去读Error,是因为我根本不关心子进程产生的error输出,所以应该有更简单的方法,就是把process.StartInfo.RedirectStandardError置为false,经测试验证,想法是对的, 这样还避免了使用线程。
在分析的过程中还对process.WaitForExit()方法产生了兴趣,从msdn的描述来看,它的作用是等待子进程结束,应该也是阻塞的,但在我的程序中它在ReadToEnd()方法后面,是否还有实际作用呢?猜测子进程结束后,ReadToEnd()方法才返回,WaitForExit()方法才被调用,但此时它已没有什么用了,肯定立刻返回,向下执行。跟踪调试了一下,验证了自己的想法,于是干脆去掉了这一句,经验证并没有对运行产生影响。
以上很大部分都是自己的猜测,并不敢保证准确,也许很多地方理解有误,欢迎各位大牛指正。
使用Process类重定向输出与错误时遇到的问题 (转)的更多相关文章
- 脚本重定向输出【错误、正确】——分析service脚本中用到的语法
<1> >&2 即 1>&2 也就是把结果输出到和标准错误一样:之前如果有定义标准错误重定向到某log文件,那么标准输出也重定向到这个log文件如:ls 2&g ...
- Linux标准输入、输出和错误和文件重定向(转) --- good
标准输入.输出和错误 当我们在shell中执行命令的时候,每个进程都和三个打开的文件相联系,并使用文件描述符来引用这些文件.由于文件描述符不容易记忆,shell同时也给出了相应的文件名.下面就是这些文 ...
- Linux标准输入、输出和错误和文件重定向 专题
当我们在shell中执行命令的时候,每个进程都和三个打开的文件相联系,并使用文件描述符来引用这些文件.由于文件描述符不容易记忆,shell同时也给出了相应的文件名. 下面就是这些文件描述符及它们通常所 ...
- C#的Process类调用第三方插件实现PDF文件转SWF文件
在项目开发过程中,有时会需要用到调用第三方程序实现本系统的某一些功能,例如本文中需要使用到的swftools插件,那么如何在程序中使用这个插件,并且该插件是如何将PDF文件转化为SWF文件的呢?接下来 ...
- 浅析Java.lang.Process类
一.概述 Process类是一个抽象类(所有的方法均是抽象的),封装了一个进程(即一个执行程序). Process 类提供了执行从进程输入.执行输出到进程.等待进程完成.检查进程的 ...
- C#的Process类的一些用法
c#之process类相关整理 一.根据进程名获取进程的用户名? 需要添加对 System.Management.dll 的引用 using System.Diagnostics; using Sys ...
- C# Process 类的思考
在这里,我先给自己留个印象 下面我们用C#实现一个调用Dos命令的小程序,让大家对系统进程能有个直观的了解.要使用Process类,首先要引入System.Diagnostic命名空间,然后定义一个新 ...
- c#之process类相关整理
一.根据进程名获取进程的用户名? 需要添加对 System.Management.dll 的引用 using System.Diagnostics; using System.Manageme ...
- 转:Process类的使用
转载自:http://www.oschina.net/code/snippet_119226_6188 一.根据进程名获取进程的用户名? 需要添加对 System.Management.dll 的引用 ...
随机推荐
- vs2013_arcgis_developer_kit_101_install
1.修改注册表以安装AE101 在注册表中HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\10.0增加类型为REG_SZ的 ...
- js根据className获取元素封装
虽然有了getElementsByClassName,但是ie低版本不支持,所以我们需要单独定义一个getByClass function getByClass(className,parent){ ...
- 网站实现特定某个地区访问执行跳转(js方法)
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- writel(readl(&pwm_timer->tcfg0) | 0xff, &pwm_timer->tcfg0);
解析这句代码什么意思! 神说:选定预分频器0 为什么? 神说:因为实验中选的是timer1,在预分频器0下: 若选择timer4,该如何写这句代码? 首先看tcfg0中选择预分频器1,在看tcfg1里 ...
- 【EF学习笔记12】----------解释查询和本地查询 区分 Enumerable 和 Queryable
简单介绍:Enumerable 和 Queryable 他们都是静态类,位于命名控件 System.Linq下,分别为IEnumerable<T>和IQueryable<T>提 ...
- 利用jquery实现自动登录
前提是需要引入jquery和jquery.cookie html 用户名:<input type="text" id="name"/><br ...
- 数据库dump导入
数据库dump导入 一.导入命令介绍: Oracle dump数据导入导出有两种方式:imp/exp.impdp/expdp.两者区别: 1.exp/imp客户端程序,受网络,磁盘的影响:impdp/ ...
- Python字典方法总结
1.清空字典中元素清空,dict变为{} L.clear()-> None. Remove all items from L 1 2 3 4 >>> L ={'shaw':2 ...
- 《算法设计手册》面试题解答 第五章:图的遍历 附:DFS应用之找挂接点
第五章面试题解答 5-31. DFS和BFS使用了哪些数据结构? 解析: 其实刚读完这一章,我一开始想到的是用邻接表来表示图,但其实用邻接矩阵也能实现啊?后来才发现应该回答,BFS用队列实现:DFS可 ...
- 动态生成dropdownlist
<td colspan=" id="td_ddl" runat="server"> </td> 后台代码: #region 动 ...