程序中要调用外部程序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类重定向输出与错误时遇到的问题 (转)的更多相关文章

  1. 脚本重定向输出【错误、正确】——分析service脚本中用到的语法

    <1> >&2 即 1>&2 也就是把结果输出到和标准错误一样:之前如果有定义标准错误重定向到某log文件,那么标准输出也重定向到这个log文件如:ls 2&g ...

  2. Linux标准输入、输出和错误和文件重定向(转) --- good

    标准输入.输出和错误 当我们在shell中执行命令的时候,每个进程都和三个打开的文件相联系,并使用文件描述符来引用这些文件.由于文件描述符不容易记忆,shell同时也给出了相应的文件名.下面就是这些文 ...

  3. Linux标准输入、输出和错误和文件重定向 专题

    当我们在shell中执行命令的时候,每个进程都和三个打开的文件相联系,并使用文件描述符来引用这些文件.由于文件描述符不容易记忆,shell同时也给出了相应的文件名. 下面就是这些文件描述符及它们通常所 ...

  4. C#的Process类调用第三方插件实现PDF文件转SWF文件

    在项目开发过程中,有时会需要用到调用第三方程序实现本系统的某一些功能,例如本文中需要使用到的swftools插件,那么如何在程序中使用这个插件,并且该插件是如何将PDF文件转化为SWF文件的呢?接下来 ...

  5. 浅析Java.lang.Process类

    一.概述      Process类是一个抽象类(所有的方法均是抽象的),封装了一个进程(即一个执行程序).      Process 类提供了执行从进程输入.执行输出到进程.等待进程完成.检查进程的 ...

  6. C#的Process类的一些用法

    c#之process类相关整理 一.根据进程名获取进程的用户名? 需要添加对 System.Management.dll 的引用 using System.Diagnostics; using Sys ...

  7. C# Process 类的思考

    在这里,我先给自己留个印象 下面我们用C#实现一个调用Dos命令的小程序,让大家对系统进程能有个直观的了解.要使用Process类,首先要引入System.Diagnostic命名空间,然后定义一个新 ...

  8. c#之process类相关整理

    一.根据进程名获取进程的用户名?   需要添加对 System.Management.dll 的引用   using System.Diagnostics; using System.Manageme ...

  9. 转:Process类的使用

    转载自:http://www.oschina.net/code/snippet_119226_6188 一.根据进程名获取进程的用户名? 需要添加对 System.Management.dll 的引用 ...

随机推荐

  1. Eclipse下修改工程名

    汇总下网上的方法. 一. 右键工程:Refactor->Rename,或选中工程按F2,修改名称 二. 右键工程:Properties->Web Project Settings,修改Co ...

  2. Devexpress VCL Build v2014 vol 15.2.3 发布

    2016年第一个版本,继续修补. New Major Features in 15.2 What's New in VCL Products 15.2 Breaking Changes To lear ...

  3. notepad++ 右键

    在网上搜索建立reg 后运行, 虽然右键菜单出现了建立的右键项目名,但与软件不关联 Windows Registry Editor Version 5.00 [HKEY_CLASSES_ROOT\*\ ...

  4. centos dns配置

    vi /etc/sysconfig/network-scripts/ifcfg-[tab两下] 新增以下修改 ONBOOT=yes  #开启自动启用网络连接 IPADDR0=192.168.21.12 ...

  5. js判断图片是否加载成功

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  6. struts1 html: textarea 不换行,变形

    <html:textarea property="summary" style="word-wrap:break-word;word-break:break-all ...

  7. 极客DIY:使用树莓派制作一套“NAS+私有云盘+下载机”

    原创作者:HackLiu 0×00 前言 ‍ ‍ 如果你家里有多台设备需要联网需要娱乐,你一定会或多或少遇到设备碎片化带来的烦恼.当然,已经有很多厂商包括新晋的小米.360在内的互联网公司做了这个事情 ...

  8. linux 无法解压过大文件解决

    [root@vmbbak yum]# unzip RHEL_5.7\ x86_64\ DVD-1.zip error: Zip file too big (greater than 429495910 ...

  9. webView.loadUrl 错误:A WebView method was called on thread 'JavaBridge'.

    String voicePath="file://"+MVOICEPATH; webView.loadUrl("javascript:voiceStopCallback( ...

  10. GridView中DropDownList

    <asp:TemplateField HeaderText="下拉框"> <ItemTemplate> <cc1:MyDropDownList ID= ...