0

I know little about C#.

I am trying to display a progressbar with the status of the background command line program.

After google examples of progressbar and run process in c#, so I'm trying to start a BackgroundWorker in the Windows Form, and run the command line program in the BackgroundWorker. I want to parse the command line's output to get the progress percentage and display it to the progress bar.

The command line program is a console program, it will output periodically its current status of progress.

But it seems it did not work. I can't readline any lines of the process's standard output.

Is there anything I missed?

Here is the raw code:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
using System.Drawing;
using System.Threading;
using System.Diagnostics;
using System.IO;


public class DemoForm : Form
{
    private BackgroundWorker backgroundWorker1;
    private Button loadButton;
    private ProgressBar progressBar1;

    public DemoForm()
    {
        InitializeComponent();

        backgroundWorker1 = new System.ComponentModel.BackgroundWorker();
        backgroundWorker1.DoWork += new System.ComponentModel.DoWorkEventHandler(
                this.backgroundWorker1_DoWork);
        backgroundWorker1.RunWorkerCompleted += 
            new System.ComponentModel.RunWorkerCompletedEventHandler(
                    this.backgroundWorker1_RunWorkerCompleted);
        backgroundWorker1.ProgressChanged += 
            new System.ComponentModel.ProgressChangedEventHandler(
                    this.backgroundWorker1_ProgressChanged);

    }

    private void loadButton_Click(object sender, EventArgs e)
    {
        backgroundWorker1.RunWorkerAsync();

        this.loadButton.Enabled = false;
    }

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        string status;
        Process p = new Process();
        p.StartInfo.FileName = "xx.exe";
        p.StartInfo.Arguments = "-q -r test.cap -p";
        p.StartInfo.UseShellExecute = false;
        p.StartInfo.RedirectStandardOutput = true;
        p.StartInfo.CreateNoWindow = true;
        p.OutputDataReceived += this.p_OutputDataReceived;
        p.EnableRaisingEvents = true;
        p.Start();
        p.BeginOutputReadLine();

/*
        while((status = p.StandardOutput.ReadLine()) != null)
        {
            Console.WriteLine(status);
            string[] words = status.Split(' ');
            int offset = Convert.ToInt32(words[0]);
            int size   = Convert.ToInt32(words[1]);

            int percentComplete = 100 * offset / size;
            MessageBox.Show(words[0], words[1]);
            backgroundWorker1.ReportProgress(percentComplete);
        }
        Console.WriteLine("wait for exit!");
        StreamReader myStreamReader = p.StandardOutput;
        status = myStreamReader.ReadLine();
        Console.WriteLine(status);
        Console.WriteLine("wait for exit!");
        */
        p.WaitForExit();
    }

    void p_OutputDataReceived(object sender, DataReceivedEventArgs e)
    {
        string status = e.Data;
        Console.WriteLine("get events");
        Console.WriteLine(status);
        string[] words = status.Split(' ');
        int offset = Convert.ToInt32(words[0]);
        int size   = Convert.ToInt32(words[1]);

        int percentComplete = 100 * offset / size;
        MessageBox.Show(words[0], words[1]);
        backgroundWorker1.ReportProgress(percentComplete);
    }

    private void backgroundWorker1_RunWorkerCompleted(object sender,
            RunWorkerCompletedEventArgs e)
    {
        progressBar1.Value = 100;
        if (e.Error == null) {
            MessageBox.Show ("Demo", "Loading completed");
        }
    }

    private void backgroundWorker1_ProgressChanged(object sender,
            ProgressChangedEventArgs e)
    {
        this.progressBar1.Value = e.ProgressPercentage;
    }

    private System.ComponentModel.IContainer components = null;

    protected override void Dispose(bool disposing)
    {
        if (disposing && (components != null))
        {
            components.Dispose();
        }
        base.Dispose(disposing);
    }

    private void InitializeComponent()
    {
        this.loadButton = new System.Windows.Forms.Button();
        this.progressBar1 = new System.Windows.Forms.ProgressBar();

        /* load button */
        this.loadButton.Location = new System.Drawing.Point(12, 12);
        this.loadButton.Name = "loadButton";
        this.loadButton.Size = new System.Drawing.Size(100, 23);
        this.loadButton.TabIndex = 0;
        this.loadButton.Text = "Load file";
        this.loadButton.UseVisualStyleBackColor = true;
        this.loadButton.Click += new System.EventHandler(this.loadButton_Click);

        /* progress bar */
        this.progressBar1.Location = new System.Drawing.Point(12, 50);
        this.progressBar1.Name = "progressBar1";
        this.progressBar1.Size = new System.Drawing.Size(100, 26);
        this.progressBar1.TabIndex = 1;

        /* Form */
        this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
        this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
        this.ClientSize = new System.Drawing.Size(133, 104);
        this.Controls.Add(this.progressBar1);
        this.Controls.Add(this.loadButton);
        this.Name = "DemoForm";
        this.Text = "DemoForm";
        this.ResumeLayout (false);
    }
}

static class Program
{
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.Run(new DemoForm());
    }
}

Or is there any better way to get a command line program's progress?

Sam Liao
  • 43,637
  • 15
  • 53
  • 61

3 Answers3

2

You have to set the WorkerReportsProgress property of your backgroundWorker to true in order to be able to report while your backgroundWorker is running:

backgroundWorker1.WorkerReportsProgress = true;
Kamyar
  • 18,639
  • 9
  • 97
  • 171
1

Building on what Kamyar was stating, you would need to then code a delegate to capture the progress of your backgroundWorker object using a UserState object. This could be any object really, but you use it to update other aspects of the GUI, in this case a status label...

this.backgroundWorker1.WorkerReportsProgress = true;
this.backgroundWorker1.WorkerSupportsCancellation = true;
this.backgroundWorker1.DoWork += new   System.ComponentModel.DoWorkEventHandler(this.backgroundWorker1_DoWork);
this.backgroundWorker1.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.backgroundWorker1_ProgressChanged);
this.backgroundWorker1.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.backgroundWorker1_RunWorkerCompleted);

private class Progress
{
   public string Status { get; set; }
   public Progress(string status)
   {
      Status = status;
   }
}

//...

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
   while(myWork)
   {
      if(backgroundWorker1.CancellationPending)
      {
         e.Cancel = true;
         break;
      }

      int p = //...percentage calc
      backgroundWorker1.ReportProgress(p, new Progress("My message...")));
   }

   e.Cancel = false;
}

void backgroundWorker1_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
{
        Progress p = (Progress)e.UserState;
        lStatus.Text = p.Status;
        progressBar.Value = e.ProgressPercentage;
}

This is just one method of implementing it though.

Thanks.

Morbo
  • 91
  • 1
  • 2
1

After manually flush the output of the command line program, problem solved.

fflush(stdout);

It's not clear why it's buffered even a line ended.

Sam Liao
  • 43,637
  • 15
  • 53
  • 61
  • 1
    I recently encountered a very similar problem. The reason behind this is explained by paxdiablo here: http://stackoverflow.com/questions/1716296/why-does-printf-not-flush-after-the-call-unless-a-newline-is-in-the-format-strin – hofan41 Feb 10 '15 at 23:40