Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何生成进程并在.NET中捕获其标准输出?_C#_.net_Process_Spawning - Fatal编程技术网

C# 如何生成进程并在.NET中捕获其标准输出?

C# 如何生成进程并在.NET中捕获其标准输出?,c#,.net,process,spawning,C#,.net,Process,Spawning,我需要生成一个子进程,它是一个控制台应用程序,并捕获其输出 我为一个方法编写了以下代码: string retMessage = String.Empty; ProcessStartInfo startInfo = new ProcessStartInfo(); Process p = new Process(); startInfo.CreateNoWindow = true; startInfo.RedirectStandardOutput = true; startInfo.Redire

我需要生成一个子进程,它是一个控制台应用程序,并捕获其输出

我为一个方法编写了以下代码:

string retMessage = String.Empty;
ProcessStartInfo startInfo = new ProcessStartInfo();
Process p = new Process();

startInfo.CreateNoWindow = true;
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardInput = true;

startInfo.UseShellExecute = false;
startInfo.Arguments = command;
startInfo.FileName = exec;

p.StartInfo = startInfo;
p.Start();

p.OutputDataReceived += new DataReceivedEventHandler
(
    delegate(object sender, DataReceivedEventArgs e)
    {
        using (StreamReader output = p.StandardOutput)
        {
            retMessage = output.ReadToEnd();
        }
    }
);

p.WaitForExit();

return retMessage;
但是,这不会返回任何内容。我不相信
OutputDataReceived
事件正在被回调,或者
WaitForExit()
命令可能正在阻塞线程,因此它永远不会回调

有什么建议吗

编辑:看来我对回调太过努力了。做:

return p.StandardOutput.ReadToEnd(); 

似乎工作正常。

设置StartInfo后,需要调用p.Start()来实际运行进程。实际上,您的函数可能挂起了WaitForExit()调用,因为该进程从未真正启动过。

以下是我验证过的代码。我使用它生成MSBuild并侦听其输出:

process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.OutputDataReceived += (sender, args) => Console.WriteLine("received output: {0}", args.Data);
process.Start();
process.BeginOutputReadLine();

看起来你的两行有问题。在设置事件处理程序以捕获输出之前,先启动流程。在添加事件处理程序之前,流程可能刚刚结束

像这样切换线路

p.OutputDataReceived += ...
p.Start();        

我刚刚尝试了这件事,以下几点对我起了作用:

StringBuilder outputBuilder;
ProcessStartInfo processStartInfo;
Process process;

outputBuilder = new StringBuilder();

processStartInfo = new ProcessStartInfo();
processStartInfo.CreateNoWindow = true;
processStartInfo.RedirectStandardOutput = true;
processStartInfo.RedirectStandardInput = true;
processStartInfo.UseShellExecute = false;
processStartInfo.Arguments = "<insert command line arguments here>";
processStartInfo.FileName = "<insert tool path here>";

process = new Process();
process.StartInfo = processStartInfo;
// enable raising events because Process does not raise events by default
process.EnableRaisingEvents = true;
// attach the event handler for OutputDataReceived before starting the process
process.OutputDataReceived += new DataReceivedEventHandler
(
    delegate(object sender, DataReceivedEventArgs e)
    {
        // append the new data to the data already read-in
        outputBuilder.Append(e.Data);
    }
);
// start the process
// then begin asynchronously reading the output
// then wait for the process to exit
// then cancel asynchronously reading the output
process.Start();
process.BeginOutputReadLine();
process.WaitForExit();
process.CancelOutputRead();

// use the output
string output = outputBuilder.ToString();
StringBuilder输出生成器;
ProcessStartInfo ProcessStartInfo;
工艺过程;
outputBuilder=新的StringBuilder();
processStartInfo=新的processStartInfo();
processStartInfo.CreateNoWindow=true;
processStartInfo.RedirectStandardOutput=true;
processStartInfo.RedirectStandardInput=true;
processStartInfo.UseShellExecute=false;
processStartInfo.Arguments=“”;
processStartInfo.FileName=“”;
流程=新流程();
process.StartInfo=processStartInfo;
//启用引发事件,因为默认情况下进程不会引发事件
process.EnableRaisingEvents=true;
//在启动流程之前,为OutputDataReceived附加事件处理程序
process.OutputDataReceived+=新的DataReceivedEventHandler
(
委托(对象发送方、DataReceivedEventArgs e)
{
//将新数据附加到已读入的数据
outputBuilder.Append(如数据);
}
);
//开始这个过程
//然后开始异步读取输出
//然后等待进程退出
//然后取消异步读取输出
process.Start();
process.BeginOutputReadLine();
process.WaitForExit();
process.CancelOutputRead();
//使用输出
字符串输出=outputBuilder.ToString();

以下是我用来运行进程并获取其输出和错误的方法:

public static string ShellExecute(this string path, string command, TextWriter writer, params string[] arguments)
    {
        using (var process = Process.Start(new ProcessStartInfo { WorkingDirectory = path, FileName = command, Arguments = string.Join(" ", arguments), UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true }))
        {
            using (process.StandardOutput)
            {
                writer.WriteLine(process.StandardOutput.ReadToEnd());
            }
            using (process.StandardError)
            {
                writer.WriteLine(process.StandardError.ReadToEnd());
            }
        }

        return path;
    }
例如:

@"E:\Temp\MyWorkingDirectory".ShellExecute(@"C:\Program Files\Microsoft SDKs\Windows\v6.0A\Bin\svcutil.exe", Console.Out);

下面是一些完整而简单的代码。当我使用它时,它工作得很好

var processStartInfo = new ProcessStartInfo
{
    FileName = @"C:\SomeProgram",
    Arguments = "Arguments",
    RedirectStandardOutput = true,
    UseShellExecute = false
};
var process = Process.Start(processStartInfo);
var output = process.StandardOutput.ReadToEnd();
process.WaitForExit();

注意,这只捕获标准输出;它不捕获标准错误。如果两者都需要,请对每个流使用。

重定向流是异步的,可能会在进程终止后继续。Umar提到要在进程终止后取消
process.CancelOutputRead()
。然而,这有可能导致数据丢失

这对我来说是可靠的:

process.WaitForExit(...);
...
while (process.StandardOutput.EndOfStream == false)
{
    Thread.Sleep(100);
}
我没有尝试这种方法,但我喜欢Sly的建议:

if (process.WaitForExit(timeout))
{
    process.WaitForExit();
}

我需要捕获stdout和stderr,并在进程没有按预期退出时使其超时。我想到了这个:

Process process = new Process();
StringBuilder outputStringBuilder = new StringBuilder();

try
{
process.StartInfo.FileName = exeFileName;
process.StartInfo.WorkingDirectory = args.ExeDirectory;
process.StartInfo.Arguments = args;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.UseShellExecute = false;
process.EnableRaisingEvents = false;
process.OutputDataReceived += (sender, eventArgs) => outputStringBuilder.AppendLine(eventArgs.Data);
process.ErrorDataReceived += (sender, eventArgs) => outputStringBuilder.AppendLine(eventArgs.Data);
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
var processExited = process.WaitForExit(PROCESS_TIMEOUT);

if (processExited == false) // we timed out...
{
    process.Kill();
    throw new Exception("ERROR: Process took too long to finish");
}
else if (process.ExitCode != 0)
{
    var output = outputStringBuilder.ToString();
    var prefixMessage = "";

    throw new Exception("Process exited with non-zero exit code of: " + process.ExitCode + Environment.NewLine + 
    "Output from process: " + outputStringBuilder.ToString());
}
}
finally
{                
process.Close();
}

我将stdout和stderr管道连接到同一个字符串中,但是如果需要,您可以将其分开。它使用事件,所以它应该在事件发生时处理它们(我相信)。我已成功运行此程序,并将很快对其进行容量测试。

来自Judah的答案对我不起作用(或不完整),因为应用程序在第一次
BeginOutputReadLine()之后退出

这对我来说是一个完整的片段,读取ping的常量输出:

        var process = new Process();
        process.StartInfo.FileName = "ping";
        process.StartInfo.Arguments = "google.com -t";
        process.StartInfo.RedirectStandardOutput = true;
        process.StartInfo.UseShellExecute = false;
        process.OutputDataReceived += (sender, a) => Console.WriteLine(a.Data);
        process.Start();
        process.BeginOutputReadLine();
        process.WaitForExit();


这是一个输入错误,当我删除一些代码来制作一个样本时,我不小心删掉了那一行。即使它没有这样标记,这可能是正确的答案。这对我不起作用。FlySwat在回答中的编辑对我有用。@Casperlonnielsen,同上。这是错误的。要实现这一点,首先需要调用开始读取stdOut流的
BeginOutputReadLine
。您也可以直接读取流。不幸的是,只在进程启动后创建IO流。调用
BeginOutputReadLine
或在调用
process.Start()
之前读取流将抛出,因为流还不存在。修复OP的关键命令是添加BeginOutputReadLine()。非常感谢,@Judah Himango我不敢相信有多少人忽略了“BeginOutputReadLine”部分。救了我的命,谢谢!要捕获错误,请添加
RedirectStandardError=true
process.ErrorDataReceived+=(发送方,args)=>Console.WriteLine(args.Data)
process.BeginErrorReadLine()奇怪的是,在Start()和BeginOutputReadLine()之间是否有一段时间,输出可能会丢失(不提供给OutputDataReceived)。我发现它们不是很可靠,有时会截断应用程序输出的开头?这已经是一个字符串变量:)我发现这稍微容易一点
p.StartInfo=StartInfo;p、 Start();输出=p.StandardOutput.ReadToEnd();p、 WaitForExit()@Spike,但它是同步运行的。如果您需要将数据输入到标准输入,那么这将不起作用请注意,EnableRaisingEvents=true似乎只是流程需要。要提升的已退出事件这非常有效。谢谢@umar farooq Khawajas应该是“附录”,这不是一个等待发生的僵局吗?MSDN文档说,如果同时侦听输出和错误,则可能会出现死锁。如果错误缓冲区已满,应用程序将停止,并等待其清空。但是直到