简体   繁体   中英

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

I have following use case. I have created one EXE in from Unmanned C++. let say printing 1 - 100 on console. output is captured into below output callback

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

However I want to use IEnumerable and yield return of e.Data. Considering the C++ exe will have it's own execution thread it won't wait for it provide the control. I will have to wait for it's execution to finish. I can capture everything and prepare a list. then I can yield items one by one. My question to you is there any known mechanism or way to do this while the process is running? what can I change in my C++ code? so that it allows me to control this back and forth? Consider this process is running under one function on the C# side.

The next approach can be used to read the output of the external process while it is running:

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.

Here are links to learn to understand this code sample:


For convenience, we can create a method that starts external process and immediately returns IEnumerable object that can be used to loop over the output data of the process:

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();
}

And then we can use this method when we need to run external process and read its output as soon as output data becomes available:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM