Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • SEARCH
  • Home
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 8987117
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: June 15, 20262026-06-15T21:42:47+00:00 2026-06-15T21:42:47+00:00

Below is my attempt to turn a System.Diagnostics.Process into an IConnectableObservable. This solution has

  • 0

Below is my attempt to turn a System.Diagnostics.Process into an IConnectableObservable.
This solution has a problem: I’d like to listen to the standard output and error continiously and use the event Process.Exited as trigger for OnCompleted. Unfortunately I found out that Process.Exited is raised before output buffers are empty. This means that without my ugly workaround with a thread sleep I can reproduce situations where the output is not served via OnNext statements.

Q1: Do you see any workaround for this issue?

Q2: With regard to System.Reactive: What could I have done better in my solution?

regards,

Markus

public static class RxProcessUtilities
{
    /// <summary>
    /// Creates a connectable observable for a process.
    /// </summary>
    /// <remarks>Must be a connectable observable in order to hinder multiple 
    /// subscriptions to call the process multiple times.</remarks>
    /// <param name="process">The process.</param>
    /// <returns></returns>
    public static IConnectableObservable<string> CreateConnectableObservableProcess
        (string filename, string arguments, IObservable<string> input = null)
    {
        var observable = Observable.Using(() =>
            {
                Process process = new Process();

                // process configuration
                process.StartInfo.FileName = filename;
                process.StartInfo.Arguments = arguments;
                process.StartInfo.CreateNoWindow = true;
                process.StartInfo.UseShellExecute = false;

                process.EnableRaisingEvents = true;
                process.StartInfo.RedirectStandardError = true;
                process.StartInfo.RedirectStandardOutput = true;

                if (null != input)
                {
                    process.StartInfo.RedirectStandardInput = true;

                    input.Subscribe(s =>
                        {
                            if (!process.HasExited)
                            {
                                process.StandardInput.Write(s);
                            }
                        });
                }

                return process;
            },
            process =>
            {
                return Observable.Create<string>(
                (IObserver<string> observer) =>
                {
                    // listen to stdout and stderr
                    var stdOut = RxProcessUtilities.CreateStandardOutputObservable(process);
                    var stdErr = RxProcessUtilities.CreateStandardErrorObservable(process);

                    var stdOutSubscription = stdOut.Subscribe(observer);
                    var stdErrSubscription = stdErr.Subscribe(observer);

                    var processExited = Observable.FromEventPattern
                    (h => process.Exited += h, h => process.Exited -= h);

                    var processError = processExited.Subscribe(args =>
                    {
                        // Here is my problem: process sends exited event *before* all 
                        // *DataReceived events have been raised

                        // My ugly workaround for process exit before stdout and stderr buffers are empty.
                        Thread.Sleep(2000);

                        // Also: AFAICS we cannot read synchronously what is left in the buffer, 
                        // since we started asynchronously. This will throw:
                        // string restOfStdOut = process.StandardOutput.ReadToEnd();
                        // string restOfStdErr = process.StandardError.ReadToEnd();

                        if (process.ExitCode != 0)
                        {
                            observer.OnError(new Exception
                                (String.Format("Process '{0}' terminated with error code {1}",
                                 process.StartInfo.FileName, process.ExitCode)));
                        }
                        else
                        {
                            observer.OnCompleted();
                        }
                    });

                    process.Start();

                    process.BeginOutputReadLine();
                    process.BeginErrorReadLine();

                    return new CompositeDisposable
                        (stdOutSubscription,
                         stdErrSubscription,
                         processError);
                });
            });

        return observable.Publish();
    }

    /// <summary>
    /// Creates an IObservable&lt;string&gt; for the standard error of a process.
    /// </summary>
    /// <param name="process">The process.</param>
    /// <returns></returns>
    public static IObservable<string> CreateStandardErrorObservable(Process process)
    {
        // var processExited = Observable.FromEventPattern
        //    (h => process.Exited += h, h => process.Exited -= h);

        var receivedStdErr =
            Observable.FromEventPattern<DataReceivedEventHandler, DataReceivedEventArgs>
                (h => process.ErrorDataReceived += h,
                 h => process.ErrorDataReceived -= h)
            //.TakeUntil(processExited) 
            // cannot be used here, since process exited event might be raised 
            // before all stderr and stdout events occurred.
            .Select(e => e.EventArgs.Data);

        return Observable.Create<string>(observer =>
        {
            var cancel = Disposable.Create(process.CancelErrorRead);

            return new CompositeDisposable(cancel, receivedStdErr.Subscribe(observer));
        });
    }

    /// <summary>
    /// Creates an IObservable&lt;string&gt; for the standard output of a process.
    /// </summary>
    /// <param name="process">The process.</param>
    /// <returns></returns>
    public static IObservable<string> CreateStandardOutputObservable(Process process)
    {
        var receivedStdOut =
            Observable.FromEventPattern<DataReceivedEventHandler, DataReceivedEventArgs>
            (h => process.OutputDataReceived += h,
             h => process.OutputDataReceived -= h)
            .Select(e => e.EventArgs.Data);

        return Observable.Create<string>(observer =>
        {
            var cancel = Disposable.Create(process.CancelOutputRead);

            return new CompositeDisposable(cancel, receivedStdOut.Subscribe(observer));
        });
    }
}
  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-06-15T21:42:48+00:00Added an answer on June 15, 2026 at 9:42 pm

    The trick is

    process.WaitForExit();
    

    See http://msdn.microsoft.com/en-us/library/fb4aw7b8.aspx: “This overload [WaitForExit()] ensures that all processing has been completed, including the handling of asynchronous events for redirected standard output. You should use this overload after a call to the WaitForExit(Int32) overload when standard output has been redirected to asynchronous event handlers.”

    Here is the complete solution:

        /// <summary>
        /// Creates a connectable observable for a process.
        /// </summary>
        /// <remarks>Must be a connectable observable in order to hinder multiple subscriptions to call the process multiple times.</remarks>
        /// <param name="process">The process.</param>
        /// <returns></returns>
        public static IConnectableObservable<string> CreateConnectableObservableProcess(string filename, string arguments, IObservable<string> input = null)
        {
            var observable = Observable.Using(() =>
                {
                    Process process = new Process();
    
                    // process configuration
                    process.StartInfo.FileName = filename;
                    process.StartInfo.Arguments = arguments;
                    process.StartInfo.CreateNoWindow = true;
                    process.StartInfo.UseShellExecute = false;
    
                    process.EnableRaisingEvents = true;
                    process.StartInfo.RedirectStandardError = true;
                    process.StartInfo.RedirectStandardOutput = true;
    
                    if (null != input)
                    {
                        process.StartInfo.RedirectStandardInput = true;
    
                        input.Subscribe(s =>
                            {
                                if (!process.HasExited)
                                {
                                    process.StandardInput.Write(s);
                                }
                            });
                    }
    
                    return process;
                },
                process =>
                {
                    return Observable.Create<string>(
                        (IObserver<string> observer) =>
                        {
                            // listen to stdout and stderr
                            var stdOut = RxProcessUtilities.CreateStandardOutputObservable(process);
                            var stdErr = RxProcessUtilities.CreateStandardErrorObservable(process);
    
                            var stdOutSubscription = stdOut.Subscribe(observer);
                            var stdErrSubscription = stdErr.Subscribe(observer);
    
                            var processExited = Observable.FromEventPattern(h => process.Exited += h, h => process.Exited -= h);
    
                            var processError = processExited.Subscribe(args =>
                            {
                                process.WaitForExit();
    
                                try
                                {
                                    if (process.ExitCode != 0)
                                    {
                                        observer.OnError(new Exception(String.Format("Process '{0}' terminated with error code {1}",
                                            process.StartInfo.FileName, process.ExitCode)));
                                    }
                                    else
                                    {
                                        observer.OnCompleted();
                                    }
                                }
                                finally
                                {
                                    process.Close();
                                }
                            });
    
                            process.Start();
    
                            process.BeginOutputReadLine();
                            process.BeginErrorReadLine();
    
                            return new CompositeDisposable(stdOutSubscription,
                                                           stdErrSubscription,
                                                           processError);
                        });
                });
    
            return observable.Publish();
        }
    
    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

I'm trying to rewrite this as a for loop; my attempt below failed. Any
How would i add key commands to the sprite plumbers? Below is my attempt
When I attempt to run the script below in Eclipse(PyDev): import subprocess subprocess.call(/usr/local/bin/mitmdump) An
Below is the code I am using and the error happens when I attempt
Below is the code from a plugin I use for sitemaps. I would like
I need to iterate from c to k alphabetically in shellscript, the attempt below
preg_match_all('/<h3>(.+?)<span>[a-z]*<\/span><\/h3>[\t\s+]*<ul class=cities>(.+?)<\/ul>[\t\s+]*<div class=clear><\/div>/is', Below is my attempt to make <h3>(.+?)<span> ungreedy according to the
After many attempts to crack this I am stuck so I turn to SO
Below is my attempt to iterate a sequence of maps; the code fails due
I have the code below in an attempt to allow the user to Step

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.