如何将 IEnumerable 与 Process.Start 的输出事件处理程序一起使用?

How to use IEnumerable with Process.Start's output event handler?

我有以下用例。我从 Unmanned C++ 创建了一个 EXE。假设在控制台上打印 1 - 100。输出被捕获到下面的输出回调

List<int> a = new List<int>();
process.OutputDataReceived += new DataReceivedEventHandler((sender, e) =>
{ 
   a.add(Convert.ToInt(e.Data));
}

但是我想使用 IEnumerable 并产生 e.Data 的 return。考虑到 C++ exe 将有它自己的执行线程,它不会等待它提供控制。我将不得不等待它的执行完成。我可以捕获所有内容并准备一份清单。然后我可以一件一件地产出物品。我想问你,在进程 运行 时,是否有任何已知的机制或方法可以做到这一点?我可以在我的 C++ 代码中更改什么?这样我就可以来回控制它?考虑这个过程在 C# 端的一个函数下 运行。

下一个方法可以用来在运行ning时读取外部进程的输出:

var proc = new Process
{
    StartInfo =
    {
        FileName = "process_name.exe",
        UseShellExecute = false,
        RedirectStandardOutput = true,
    },
    // Set this property to true to enable external
    // process raise notification that is has exited.
    EnableRaisingEvents = true
};

// BlockingCollection is a thread safe producer-consumer
// collection. We use it to collect the output of the
// external process and to process it while the process
// is running.
var lines = new BlockingCollection<string>();

proc.OutputDataReceived += (s, e) =>
{
    // Here we collect the output of the external process.
    if (!string.IsNullOrEmpty(e.Data))
        lines.Add(e.Data);
};

// This event is raised when external process exits.
proc.Exited += (s, e) =>
{
    // Here we notify our BlockingCollection that no more
    // data to process is available. 
    lines.CompleteAdding();
};

proc.Start();
proc.BeginOutputReadLine();

// Here we start to process the output of the external process
// without waiting for it to exit.
// This loop iterates over the items produced by the
// BlockingCollection until method CompleteAdding is called.
// This loop is being executed while external process is
// running and finishes when the process exits.
foreach (string line in lines.GetConsumingEnumerable())
{
    Console.WriteLine(line);
}

// Here we do not need to call proc.WaitForExit(), because
// loop over lines collection finishes when proc exits.

以下是学习理解此代码示例的链接:


为方便起见,我们可以创建一个启动外部进程的方法,并立即 returns IEnumerable 可用于循环处理进程输出数据的对象:

private static IEnumerable<string> ReadOutput(string procFileName)
{
    var proc = new Process
    {
        StartInfo =
        {
            FileName = procFileName,
            UseShellExecute = false,
            RedirectStandardOutput = true,
        },
        EnableRaisingEvents = true
    };

    var lines = new BlockingCollection<string>();

    proc.OutputDataReceived += (s, e) =>
    {
        if (!string.IsNullOrEmpty(e.Data))
            lines.Add(e.Data);
    };

    proc.Exited += (s, e) =>
    {
        lines.CompleteAdding();
    };

    proc.Start();
    proc.BeginOutputReadLine();

    return lines.GetConsumingEnumerable();
}

然后当我们需要运行外部进程并在输出数据可用时立即读取其输出时,我们可以使用此方法:

public static void Demo()
{
    foreach (string line in ReadOutput("process_name.exe"))
    {
        Console.WriteLine(line);
    }
}