电脑故障

位置:IT落伍者 >> 电脑故障 >> 浏览文章

如何获取子进程的输出


发布日期:2023/10/2
 

经常看到论坛中有人问到当用Process组件启动新的进程后如何获取它的输出的问题采取将子进程的输出定向到一个临时文件中当然也可以解决问题但是这样每次父进程从临时文件中获取信息后还要删除该临时文件毕竟比较麻烦其实Process提供了几个属性可以获取输出框架sdk的帮助文档里面就有这方面的例子但是对于如何同时获取错误输出和标准输出方面没有给出具体代码本文将给出实例并对管道的特性作一些说明

获取子进程标准输出和错误输出的的方法

我们写一个小程序pcs用它来产生标准输出和错误输出

//pcs代码如下

using System;

class class

{

public static void Main()

{

int i = ;

string s = StringFormat(out:{}i);

SystemConsoleOutWriteLine(s);

string s = StringFormat(err:{}**************************************************i);

SystemConsoleErrorWriteLine(s);

}

}

编译成pexe

获取子进程的标准输出和错误输出的源程序

//pcs

Process p = new Process(pexe);

pStartInfoUseShellExecute = false; //指定不使用系统的外壳程序而是直接启动被调用程序本身

pStartInfoRedirectStandardOutput = true; //只有将此属性设为true才能通过管道获取子进程输出

pStartInfoRedirectStandardError = true;

pStart(); //启动子进程

string output = pStandardOutputReadToEnd(); //读取标准输出

string error = pStandardErrorReadToEnd(); //读取错误输出

pWaitForExit(); //等待子进程执行完毕

上例中父进程启动子进程后就等待着从管道中取走标准输出取走全部标准输出后取走错误输出我们运行起来没有任何错误但是一旦我门增加pexe中的标准输出和错误输出的字节数

for(i=;i<;i++)

SystemConsoleOutWriteLine(s);

for(i=;i<;i++)

SystemConsoleErrorWriteLine(s);

编译后再运行就会发现父进程和子进程出现了死锁只有强行关闭才能终止进程

管道

Process 组件通过管道与子进程进行通讯如果同时重定向标准输出和标准错误然后试图读取它们当管道被填满时候就会出现问题上例中父进程只有读完了所有的标准输出才能读错误输出而子进程的标准输出每当把管道填满时候父进程都会取走子进程接着向管道输出后续的标准输出这都运行的很好可是当子进程执行到输出错误输出的时候由于子进程还没有运行结束它的标准输出流就不会处于结束状态(虽然在我们的例子中它的次循环的标准输出已经执行完毕)这就导致父进程中的ReadToEnd()方法不会执行完毕所以父进程中的pStandardErrorReadToEnd()无法执行从而管道中的错误输出无法被读取子进程的后续错误输出无法写入管道最终子进程和父进程彼此无限等待出现了阻塞情况

子进程通过检查管道中最后一个字节是否被取走来决定是否输出后续内容也就是说如果管道缓沖区中最后一个字节之前的所有内容都被取走子进程仍然会处于等待中如果前面所有字节都没有取走但最后一个字节内容被取走子进程会继续向管道里输出后续内容当然这样只能输出一字节到管道中最后位置

我们如果把pcs中的string output = pStandardOutputReadToEnd()改为

for(int i=;i<*;i++)

ConsoleWrite((char) pStandardOutputRead());

就不会出现死锁情况因为当获取标准输出的循环执行完后会接着执行获取错误输出的语句而不需等待标准输出流的结束但是这种方法毫无实际意义因为实际中我们并不可能像本例中知道标准输出会有多少字节

用多线程分别获取标准输出和错误输出

对于这种情况框架文档中建议这样解决创建两个线程以便应用程序可以在单独的线程上读取每个流的输出下面给出例子:

//pcs:

class class

{

public static void Main()

{

ProcessStartInfo pi = new ProcessStartInfo();

piFileName = @c:\pexe;

piUseShellExecute = false;

piRedirectStandardOutput = true;

piRedirectStandardError = true;

SystemDiagnosticsProcess p = new Process();

pStartInfo = pi;

pStart();

tout t = new tout(p);

terr t = new terr(p);

Thread thread = new Thread(new ThreadStart(tRead));

Thread thread = new Thread(new ThreadStart(tRead));

threadStart();

threadStart();

pWaitForExit();

ConsoleWriteLine(pexe结束);

}

}

class tout

{

Process p;

public tout(Process p)

{

thisp = p;

}

public void Read()

{

int a = ;

while((a = pStandardOutputRead()) > )

{

ConsoleWrite( ((char) a)ToString() );

}

ThreadCurrentThreadAbort();

return;

}

}

class terr

{

Process p;

public terr(Process p)

{

thisp = p;

}

public void Read()

{

int a = ;

while((a = pStandardErrorRead()) > )

{

ConsoleWrite(((char) a)ToString());

}

ThreadCurrentThreadAbort();

return;

}

}

上一篇:DotNet的学习步骤

下一篇:用LINQ做成的RSS查看器