C# Баг progress bar.

189
20 июля 2018, 20:30

имеем небольшой кусок кода, который по клику на кнопку вызывает cmd, и в ней исполняет apache bench. Надо показать прогресс бар. Я делаю так, кнопка нажата, старт таймеру, на каждый тик увеличивает значение прогресс бара на 1 проблема в том, что прогресс бар начинает бежать уже после того как отработает CMD и данные выгрузятся в текст бокс.

private void button1_Click(object sender, EventArgs e)
            {
                timer1.Start();
                string argumets = @"/k ab";  // это строчка запускает саму apache bench
                string requests = " -n "; /// объявляем переменные
                string concurrency = " -c ";
                string adress = " http://";
                string timeLimit = "";
                string result = " -e filename";
                string outputData = "";

                concurrency += TextBoxConcurrency.Text.ToString();
                requests += TextBoxRequests.Text.ToString();
                adress += TextBoxServerAdress.Text.ToString() + "/";
                argumets += requests + concurrency + timeLimit;
                if (checkBoxSave.Checked == true)
                {
                    argumets += result;

                }
                else { }
                argumets += adress;

                if (checkBoxTimeLimit.Checked == true)
                {
                    timeLimit += "-t" + textBoxTimeLimit.Text.ToString();
                }
                else { }

                ProcessStartInfo psi = new ProcessStartInfo();
                //Имя запускаемого приложения
                psi.FileName = "cmd";
                //команда, которую надо выполнить
                psi.Arguments = argumets;
                //  /c - после выполнения команды консоль закроется
                //  /к - не закрывать консоль после выполнения команды
                psi.UseShellExecute = false;
                psi.RedirectStandardOutput = true;
               psi.RedirectStandardInput = true;
                psi.CreateNoWindow = true;
                var proc = Process.Start(psi);
                proc.StandardInput.WriteLine("exit");
               outputData = proc.StandardOutput.ReadToEnd();

               resultTextBox.Text = outputData;
                resultTextBox.Text = resultTextBox.Text.Remove(0, 201);
            }
            private void timer1_Tick(object sender, EventArgs e)
            {
              progressBar1.Value +=1;
            }
Answer 1

Файл ab.exe я добавил в проект и в его свойствах Copy if newer

public partial class FormMain : Form
{
    private ProcessStartInfo _startInfo;
    private System.Windows.Forms.Timer _timer;
    public FormMain()
    {
        InitializeComponent();
        this.Load += FormMain_Load;
    }
    private void FormMain_Load(object sender, EventArgs e)
    {
        var dir = Path.GetDirectoryName(this.GetType().Assembly.Location);
        var path = Path.Combine(dir, "ab.exe");
        _startInfo = new ProcessStartInfo()
        {
            FileName = path,
            Arguments = @"-n 100 -c 10 http://www.yandex.ru/",
            RedirectStandardOutput = true,
            UseShellExecute = false,
            CreateNoWindow = true
        };

        _timer = new System.Windows.Forms.Timer();
        _timer.Interval = 10;
        _timer.Tick += _timer_Tick;
    }
    private async void _buttonStart_Click(object sender, EventArgs e)
    {
        _buttonStart.Enabled = false;
        _textBox.Text = String.Empty;
        try
        {
            _timer.Start();
            var getit = await Task.Run(() => RunAB());
        }
        finally
        {
            _timer.Stop();
            _progressBar.Value = 0;
            _buttonStart.Enabled = true;
        }
    }
    private bool RunAB()
    {
        using (Process ab = new Process())
        {
            ab.StartInfo = _startInfo;
            ab.OutputDataReceived += ab_OutputDataReceived;
            ab.Start();
            ab.BeginOutputReadLine();
            ab.WaitForExit();
        }
        return true;
    }
    private void ab_OutputDataReceived(object sender, DataReceivedEventArgs e)
    {
        this.Invoke((Action)(() => _textBox.Text += e.Data + Environment.NewLine));
    }
    private void _timer_Tick(object sender, EventArgs e)
    {
        if (_progressBar.Value + 10 >= _progressBar.Maximum)
        {
            _progressBar.Value = _progressBar.Minimum;
        }
        _progressBar.Value += 1;
    }
}
Answer 2

Проблема в вашем коде в том, что вот эта строка

outputData = proc.StandardOutput.ReadToEnd();

выполняется синхронно и блокирует UI-поток на время своего выполнения. События таймера System.Windows.Forms.Timer обрабатываются в UI-потоке, а он заблокирован чтением из выходного потока процесса.

Запоздал с ответом, поэтому немного модернизировал код из ответа от @Bulson.

Убрал искусственную асинхронность через Task.Run и заменил на естественную, которая предусмотрена "из коробки". Для этого использовано событие Process.Exited, в обработчике которого вызывается Dispose для процесса и остановка таймера. Кроме того убран using при создании процесса и ожидание завершения с помощью Process.WaitForExit() так как в них больше нет необходимости.

Результат тот же, так что в целом это уже вопрос вкуса и персональных предпочтений.

public partial class FormMain : Form
{
    private ProcessStartInfo _startInfo;
    private Timer _timer;
    public FormMain()
    {
        InitializeComponent();
        Load += FormMain_Load;
    }
    private void FormMain_Load(object sender, EventArgs e)
    {
        var dir = @"<путь к файлу ab.exe>";
        var path = Path.Combine(dir, "ab.exe");
        _startInfo = new ProcessStartInfo()
        {
            FileName = path,
            Arguments = @"-n 100 -c 10 http://www.yandex.ru/",
            RedirectStandardOutput = true,
            UseShellExecute = false,
            CreateNoWindow = true
        };

        _timer = new Timer();
        _timer.Interval = 10;
        _timer.Tick += _timer_Tick;
    }
    private void _buttonStart_Click(object sender, EventArgs e)
    {
        _buttonStart.Enabled = false;
        _textBox.Text = string.Empty;
        Process ab = new Process();
        try
        {
            _timer.Enabled = true;
            ab.StartInfo = _startInfo;
            ab.OutputDataReceived += ab_OutputDataReceived;
            ab.EnableRaisingEvents = true;
            ab.Exited += Ab_Exited;
            ab.Start();
            ab.BeginOutputReadLine();
        }
        catch
        {
            _timer.Enabled = false;
            _progressBar.Value = 0;
            _buttonStart.Enabled = true;
            ab.Dispose();
        }
    }
    private void Ab_Exited(object sender, EventArgs e)
    {
        (sender as Process).Dispose();
        Invoke((Action)(() => 
        {
            _timer.Enabled = false;
            _progressBar.Value = 0;
            _buttonStart.Enabled = true;
        }));
    }
    private void ab_OutputDataReceived(object sender, DataReceivedEventArgs e)
    {
        Invoke((Action)(() => _textBox.AppendText(e.Data + Environment.NewLine)));
    }
    private void _timer_Tick(object sender, EventArgs e)
    {
        if(_progressBar.Value + 10 >= _progressBar.Maximum)
        {
            _progressBar.Value = _progressBar.Minimum;
        }
        _progressBar.Value += 1;
    }
}
READ ALSO
Блок catch не выполняется

Блок catch не выполняется

Выполняю удаленную отладку сайта, который находится на веб-сервере IIS

174
UploadFile Telegram bot API

UploadFile Telegram bot API

Как я могу загрузить файл через WebClientUploadFile на сервера Telegram? И потом отправить его пользователю?

426
Выбор нужного текста

Выбор нужного текста

Не могу решить одну задачу

232
Из String [ ] в byte[ ] - C#

Из String [ ] в byte[ ] - C#

Всем привет! Опишу суть проблемы есть массив string в который записаны адреса байтов по типу:

209