C# 调用外部进程的类网上可以搜出很多来为什么要再写一遍实在是因为最近从网上拷贝了一个简单的例程用到项目中运行有问题后来研究了半天才解决了这些问题于是打算写这么一篇博文一来说说调用一个外部进程这么简单的一件事究竟会有哪些问题二来也希望我写的这个相对比较完整的类可以为软件开发的同道们节约一些脑细胞以便集中优势兵力解决那些真正高深复杂的软件问题
在开始正题之前我们先来看一看网上比较常见的执行外部进程的函数
private string RunCmd(string command)
{
//例Process
Process p = new Process();
pStartInfoFileName = cmdexe; //确定程序名
pStartInfoArguments = /c + command; //确定程式命令行
pStartInfoUseShellExecute = false; //Shell的使用
pStartInfoRedirectStandardInput = true; //重定向输入
pStartInfoRedirectStandardOutput = true; //重定向输出
pStartInfoRedirectStandardError = true; //重定向输出错误
pStartInfoCreateNoWindow = true; //设置置不显示窗口
pStart(); //
//pStandardInputWriteLine(command); //也可以用这种方式输入命令
//pStandardInputWriteLine(exit); //要得加上Exit
return pStandardOutputReadToEnd(); //输出出流取得命令行结果
}
这个方法应该是比较常见的调用外部进程的方法我以前也一直是这样调用外部进程的也没有碰到过什么问题但这次调用的外部进程比较特殊用这种方法调用就出现了两个问题
第一个问题是这个被调用的外部进程有时候会出现异常出现异常后Windows会弹出错误报告框程序于是吊死在那里必须手工干预这个问题比较好解决程序中设置一下注册表搞定
第二个问题是调用这个外部进程(是一个控制台进程)后程序会阻塞在pStandardOutputReadToEnd()这一句永远无法出来被调用的那个控制台程序也被吊死但该控制台进程在CMD 中是可以正常执行的后来看来一些资料才发现原来原因是出在该控制台程序控制台输出大量字符串管道重定向后调用程序没有及时将管道中的输出数据取出结果导致管道被阻塞程序吊死在这里还有另外一个问题虽然这次没有遇到但网上有其他人遇到就是错误信息管道不及时取出数据也会被阻塞而且如果要同时取出两个管道的数据必须要利用一个辅助线程才能实现
问题讲完了下面给出这个类的完整代码
using System;
using SystemCollectionsGeneric;
using SystemText;
using SystemRuntimeInteropServices;
using SystemThreading;
namespace LaboratoryProcess
{
class ReadErrorThread
{
SystemThreadingThread m_Thread;
SystemDiagnosticsProcess m_Process;
String m_Error;
bool m_HasExisted;
object m_LockObj = new object();
public String Error
{
get
{
return m_Error;
}
}
public bool HasExisted
{
get
{
lock (m_LockObj)
{
return m_HasExisted;
}
}
set
{
lock (m_LockObj)
{
m_HasExisted = value;
}
}
}
private void ReadError()
{
StringBuilder strError = new StringBuilder();
while (!m_ProcessHasExited)
{
strErrorAppend(m_ProcessStandardErrorReadLine());
}
strErrorAppend(m_ProcessStandardErrorReadToEnd());
m_Error = strErrorToString();
HasExisted = true;
}
public ReadErrorThread(SystemDiagnosticsProcess p)
{
HasExisted = false;
m_Error = ;
m_Process = p;
m_Thread = new Thread(new ThreadStart(ReadError));
m_ThreadStart();
}
}
class RunProcess
{
private String m_Error;
private String m_Output;
public String Error
{
get
{
return m_Error;
}
}
public String Output
{
get
{
return m_Output;
}
}
public bool HasError
{
get
{
return m_Error != && m_Error != null;
}
}
public void Run(String fileName String para)
{
StringBuilder outputStr = new StringBuilder();
try
{
//disable the error report dialog
//reference: http://wwwdevcowcom/blogs/adnrg/archive////DisableErrorReportingDialogforyourapplicationwiththeregistryaspx
MicrosoftWinRegistryKey key;
key = MicrosoftWinRegistryLocalMachineOpenSubKey(@software\microsoft\PCHealth\ErrorReporting\ true);
int doReport = (int)keyGetValue(DoReport);
if (doReport != )
{
keySetValue(DoReport );
}
int showUI = (int)keyGetValue(ShowUI);
if (showUI != )
{
keySetValue(ShowUI );
}
}
catch
{
}
m_Error = ;
m_Output = ;
try
{
SystemDiagnosticsProcess p = new SystemDiagnosticsProcess();
pStartInfoFileName = fileName;
pStartInfoArguments = para;
pStartInfoUseShellExecute = false;
pStartInfoRedirectStandardInput = true;
pStartInfoRedirectStandardOutput = true;
pStartInfoRedirectStandardError = true;
pStartInfoCreateNoWindow = true;
pStart();
ReadErrorThread readErrorThread = new ReadErrorThread(p);
while (!pHasExited)
{
outputStrAppend(pStandardOutputReadLine()+\r\n);
}
outputStrAppend(pStandardOutputReadToEnd());
while (!readErrorThreadHasExisted)
{
ThreadSleep();
}
m_Error = readErrorThreadError;
m_Output = outputStrToString();
}
catch (Exception e)
{
m_Error = eMessage;
}
}
}
}