имеем небольшой кусок кода, который по клику на кнопку вызывает 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;
}
Файл 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;
}
}
Проблема в вашем коде в том, что вот эта строка
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;
}
}
Кофе для программистов: как напиток влияет на продуктивность кодеров?
Рекламные вывески: как привлечь внимание и увеличить продажи
Стратегії та тренди в SMM - Технології, що формують майбутнє сьогодні
Выделенный сервер, что это, для чего нужен и какие характеристики важны?
Современные решения для бизнеса: как облачные и виртуальные технологии меняют рынок
Выполняю удаленную отладку сайта, который находится на веб-сервере IIS
Как я могу загрузить файл через WebClientUploadFile на сервера Telegram? И потом отправить его пользователю?
Всем привет! Опишу суть проблемы есть массив string в который записаны адреса байтов по типу: