0

I try to collect the real time output from "public async Task RunProcessAsync()" and send to Form1 via "synchronizationContext.Post()" and "progress.Report()" to process there, for debug purpose, timestamps are added both in the output and received data. the RunProcessAsync() code is modify from https://gist.github.com/NSouth/6d44d07db97df7d41bce33ac3117fdeb.
Received data are store in Form1 and print them using btnShow()

It is found that progress.Report() send out the data at the same time as stdOutBuilder.Append(), but synchronizationContext.Post() send after RunProcessAsync(). Both data arrive Form1 after RunProcessAsync() completed.

I try to add "System.Windows.Forms.Application.DoEvents();", but no improvement.

These are the data collected with timestamp

processStartInfo.FileName = @"C:\WINDOWS\system32\cmd.exe";  
processStartInfo.Arguments = @"/k Dir C:\  /on ";

stdOutBuilder.Append()
08:05.407  16/07/2020  22:58    <DIR>          AndroidStudio  
08:05.407  28/06/2020  21:03    <DIR>          App  
08:05.408  21/06/2017  20:50    <DIR>          MP_Upload  
  
VS2013 debug Output  
08:05.407  16/07/2020  22:58    <DIR>          AndroidStudio  
08:05.407  28/06/2020  21:03    <DIR>          App  
08:05.408  21/06/2017  20:50    <DIR>          MP_Upload  
  
m_SynchronizationContext.Post
08:05.452  08:05.452  16/07/2020  22:58    <DIR>          AndroidStudio  
08:05.453  08:05.453  28/06/2020  21:03    <DIR>          App  
08:05.453  08:05.453  21/06/2017  20:50    <DIR>          MP_Upload  
  
progress.Report
08:05.452  08:05.407  16/07/2020  22:58    <DIR>          AndroidStudio  
08:05.453  08:05.408  28/06/2020  21:03    <DIR>          App  
08:05.454  08:05.408  21/06/2017  20:50    <DIR>          MP_Upload  
^^^^^      ^^^^^------- timestamp same as stdOutBuilder.Append()  
^^^^^------ arrive after RunProcessAsync() completed  
  

Can anyone help me so that Form1 can receive data in real time?

Below are code used:


public async Task<Result> RunProcessAsync(ProcessStartInfo startInfo, IProgress<ExecuteDosCmd.Form1.ProgressObject> progress, string stdIn = null, int? timeoutMs = null)
// modify from https://gist.github.com/NSouth/6d44d07db97df7d41bce33ac3117fdeb
{
......

    process.OutputDataReceived += (s, e) =>
    {
        if (e.Data == null)
        {
            stdOutCloseEvent.TrySetResult(true);
        }
        else
        {   
            stdOutBuilder.Append(DateTime.Now.ToString("mm:ss.fff  ") + e.Data +"\n");
            //08:05.407  16/07/2020  22:58    <DIR>          AndroidStudio
            //08:05.407  28/06/2020  21:03    <DIR>          App
            //08:05.408  21/06/2017  20:50    <DIR>          MP_Upload

            debugPr(e.Data);
            //08:05.407  16/07/2020  22:58    <DIR>          AndroidStudio
            //08:05.407  28/06/2020  21:03    <DIR>          App
            //08:05.408  21/06/2017  20:50    <DIR>          MP_Upload

            m_SynchronizationContext.Post(_ => ExecuteDosCmd.Form1.form1.updateProgress01(DateTime.Now.ToString("mm:ss.fff  ") + e.Data + "\n"), null);
            //08:05.452  08:05.452  16/07/2020  22:58    <DIR>          AndroidStudio
            //08:05.453  08:05.453  28/06/2020  21:03    <DIR>          App
            //08:05.453  08:05.453  21/06/2017  20:50    <DIR>          MP_Upload
            
            //System.Windows.Forms.Application.DoEvents(); 
            progress.Report(new ExecuteDosCmd.Form1.ProgressObject() { Percentage = 20, Message = DateTime.Now.ToString("mm:ss.fff  ") + e.Data + "\n" });
            //08:05.452  08:05.407  16/07/2020  22:58    <DIR>          AndroidStudio
            //08:05.453  08:05.408  28/06/2020  21:03    <DIR>          App
            //08:05.454  08:05.408  21/06/2017  20:50    <DIR>          MP_Upload
            ^^^^^        ^^^^^------- timestamp same as stdOutBuilder.Append()   
            ^^^^^------ arrive after RunProcessAsync() completed                                

            //System.Windows.Forms.Application.DoEvents(); 
            
        }
    };
    processTasks.Add(stdOutCloseEvent.Task);
    ......
}   

// Form1

    public class ProgressObject 
    {
        public int Percentage { get; set; }
        public string Message { get; set; }
    }
    string str2 = "";
    private void updateProgress02(ProgressObject prog)
    {
        //debugPr(prog.Message);
        str2 += (DateTime.Now.ToString("mm:ss.fff  ") + prog.Message);
        rtbError.AppendText(DateTime.Now.ToString("mm:ss.fff  ") + prog.Message);
    }

    private void debugPr(String s)
    {
        System.Diagnostics.Debug.WriteLine(DateTime.Now.ToString("mm:ss.fff  ") + s);   // Output will be in Visual Studio debug window or Dbgview.exe
    }

    string str = "";
    public void updateProgress01(string msg)
    {
        str += (DateTime.Now.ToString("mm:ss.fff  ") + msg );
        progressBar1.Value = progressBar1.Value + 1;
        debugPr(""+progressBar1.Value);
    }

    private void btnShow_Click(object sender, EventArgs e)
    {
        rtbOutput.AppendText("\n\n"+DateTime.Now.ToString("mm:ss.fff  ") + "\n\nm_SynchronizationContext.Post\n");
        rtbOutput.AppendText(str);
        rtbOutput.AppendText("\n\nprogress.Report\n");
        rtbOutput.AppendText(str2);
    }
            
    private void btnStart_Click(object sender, EventArgs e)
    {
        debugPr("ProcessAsyncHelper03");
        ProcessAsyncHelper03 ph = new ProcessAsyncHelper03();
        Task<ExecuteDosCmd.ProcessAsyncHelper03.Result> task3 = ph.RunProcessAsync(psi, new Progress<ProgressObject>(updateProgress02), null, 2000);
        rtbOutput.Text = task3.Result.StdOut;
        rtbError.AppendText(task3.Result.StdErr);
        rtbError.AppendText("\nExitCode: " + task3.Result.ExitCode +"\n\n");
    }

Filburt
  • 17,626
  • 12
  • 64
  • 115
  • 1
    [Don't Block on Async Code](https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html) -- Remove all these things: `rtbOutput.Text = task3.Result.StdOut;`. Why are you mixing SynchronizationContext and Progress? Is it for a test? See some simple examples [here](https://stackoverflow.com/a/62449011/7444103). – Jimi Jul 18 '20 at 10:27
  • You can make those event handlers (as `btnStart_Click`) `async`. Or run the Task. – Jimi Jul 18 '20 at 10:36
  • @Jimi It is a test, I want to find a method to get the real time output in async task for process outside the task. – user3660333 Jul 19 '20 at 02:25
  • @Jimi I added async in "private void btnShow_Click(object sender, EventArgs e)" to "private async void btnShow_Click(object sender, EventArgs e)" but have the same result. – user3660333 Jul 19 '20 at 02:31
  • If you're testing **a Process output**, you don't really need Tasks: the Process raises events, which are of course asynchronous. If you **want to the use a Task**, then you, as already mentioned and as you should have read from the post I linked, cannot block (use `Result`). Change your Task in public `async Task RunProcessAsync(int processId, IProgress progress) { }` use just the `Prograss` object to report progress (you're not using the other parameters, pass the `Process.Id` to identify the Process you want to test, or its Name, depending on how you want to handle it). – Jimi Jul 19 '20 at 02:42
  • Make the `btnStart_Click` handler `async` and `await RunProcessAsync()`, passing an `Progress` object **that you have created beforehand**. – Jimi Jul 19 '20 at 02:42

0 Answers0