【问题标题】:Run process in BackgroundWorker在 BackgroundWorker 中运行进程
【发布时间】:2014-02-12 20:04:36
【问题描述】:

我对 C# 知之甚少。

我正在尝试显示带有后台命令行程序状态的进度条。

在谷歌的进度条示例和c#中运行进程之后,我试图在Windows窗体中启动一个BackgroundWorker,并在BackgroundWorker中运行命令行程序。我想解析命令行的输出以获取进度 百分比并将其显示到进度条。

命令行程序是一个控制台程序,它会定期输出当前的进度状态。

但它似乎不起作用。我无法读取进程标准输出的任何行。

有什么我错过的吗?

这是原始代码:

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

或者有没有更好的方法来获取命令行程序的进度?

【问题讨论】:

  • 您正在运行的这个进程是什么?它是一个控制台应用程序吗?它如何报告进度?
  • 是我自己还是评论区是更新的部分?
  • 那么“问题”是没有取得进展还是“无法读取进程标准输出的任何行”?最好提出一个 minimal 失败的测试用例。
  • @pst,它无法读取输出。
  • @arsane: 你确定 word[0] 和 word[1] 转换成 Int32 没有任何错误吗?

标签: c#


【解决方案1】:

您必须将 backgroundWorker 的 WorkerReportsProgress 属性设置为 true 才能在 backgroundWorker 运行时进行报告:

backgroundWorker1.WorkerReportsProgress = true;

【讨论】:

  • 似乎到目前为止我无法获得任何输出...在报告进度之前。
  • 这不是问题所说的。
【解决方案2】:

手动刷新命令行程序的输出后,问题解决了。

fflush(stdout);

不清楚为什么即使一行结束也会缓冲。

【讨论】:

【解决方案3】:

基于 Kamyar 所说的,您需要编写一个委托来使用 UserState 对象捕获您的 backgroundWorker 对象的进度。这实际上可以是任何对象,但您可以使用它来更新 GUI 的其他方面,在本例中是状态标签...

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

这只是实现它的一种方法。

谢谢。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多