Console app async control

  • Thread starter Alistair George
  • Start date
A

Alistair George

Hi all.
A win gui has to run a console (cmd) app, desirably the cmd window is
hidden, which I will do by placing outside the view area.
The GUI must be able to parse the output from cmd and do various things
like show a progress meter etc. It should also be able to gracefully
close the cmd window if it hangs or user needs the thread to abort.
Would I be correct in assuming the cmd should be started in a thread
with async coms?
MethodInvoker with BeginInvoke?
Appreciate any pointers to articles on this subject (google search
reveals few).
Thank you.
 
M

Marc Gravell

If you look at ProcessStartInfo, you can suppress the visual console
(IIRC) with CreateNoWindow = true, and capture the output by setting
RedirectStandardOutput = false, UseShellExecute = false, and reading
from Process.StandardOutput once you have started the command.

When running a win-ui, the easiest option is to do the reading (from
StandardOutput) on a workder thread (not the UI thread), and then
(when you read something interesting) use yourForm.BeginInvoke() to
notify the UI. There is also an event-driven mechanism
(Process.OutputDataReceived), but this is trickier.

I'll put an example together if you like...

Marc
 
M

Marc Gravell

(exe)

using System;
using System.IO;

class Program {
static int Main(string[] args) {
try {
string root = args.Length == 1 ? args[0] :
Environment.CurrentDirectory;
string[] dirs = Directory.GetDirectories(args[0], "*.*",
SearchOption.AllDirectories);
int count = dirs.Length, lastPercent = -1;
for (int i = 0; i < count; i++) {
int percent = (i * 100) / count;
if (percent != lastPercent) {
Console.WriteLine(percent.ToString() + "%");
}
foreach (string file in Directory.GetFiles(dirs)) {
Console.WriteLine(file);
}
}
return 0;
} catch (Exception ex) {
Console.Error.WriteLine(ex.Message);
return -1;
}
}
}

(winform)

using System;
using System.Windows.Forms;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Text.RegularExpressions;

class SomeForm : Form {
static void Main() {
Application.EnableVisualStyles();
using (Form f = new SomeForm()) {
Application.Run(f);
}
}

Button goStop;
BackgroundWorker worker;
const string BUTTON_TEXT = "Go";
protected override void OnLoad(EventArgs e) {
base.OnLoad(e);
goStop = new Button();
goStop.Text = BUTTON_TEXT;
goStop.Click += new EventHandler(goStop_Click);
Controls.Add(goStop);
worker = new BackgroundWorker();
worker.WorkerSupportsCancellation = true;
worker.WorkerReportsProgress = true;
worker.DoWork+=new DoWorkEventHandler(worker_DoWork);
worker.ProgressChanged += new
ProgressChangedEventHandler(worker_ProgressChanged);
worker.RunWorkerCompleted += new
RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
}

void worker_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e) {
goStop.Text = BUTTON_TEXT;
if (e.Cancelled) {
this.Text = "cancelled";
} else if ((int) e.Result == 0) {
this.Text = "complete";
} else {
this.Text = "error " + e.Result.ToString();
}
}

void worker_ProgressChanged(object sender,
ProgressChangedEventArgs e) {
goStop.Text = e.ProgressPercentage.ToString() + "%";
}

void goStop_Click(object sender, EventArgs e) {
if (worker.IsBusy) {
worker.CancelAsync();
} else {
worker.RunWorkerAsync();
}
}

void worker_DoWork(object sender, DoWorkEventArgs e)
{
string exe = @"c:\walker.exe", root = @"c:\develop\t";
ProcessStartInfo psi = new ProcessStartInfo(exe, root);
psi.UseShellExecute = false;
psi.RedirectStandardOutput = true;
psi.CreateNoWindow = true;
using(Process proc = Process.Start(psi))
using(StreamReader reader = proc.StandardOutput) {
worker.ReportProgress(0);
Regex re = new Regex(@"\d{1,3}%");
int index = 0;
while (!reader.EndOfStream) {
if (worker.CancellationPending) {
proc.Kill();
e.Cancel = true;
break;
}
string line = reader.ReadLine();
if (re.IsMatch(line)) {

worker.ReportProgress(int.Parse(line.TrimEnd('%')));
} else {
if (index++ % 20 == 0) {
this.Invoke((MethodInvoker)delegate {
this.Text = line;
});
}
}
}
if (!e.Cancel) {
e.Result = proc.ExitCode;
}

}
}
}
 
A

Alistair George

Marc said:
I'll put an example together if you like...

Marc
Thank you so much Marc. You have made the task very easy for me to
implement. On the few google results that I have read on the subject, it
is indicated that getting the output from the CMD window is unreliable
to say the least. This is a problem that the previous implimentation
had. So it will be interesting to see how well it works if you like I
will stay in touch.

Am trying to improve a situation whereby the previous method used to
occasionally hang when they used the cmd output. I am not sure whether
they used sync or async comms in that case.
Thanks again, Al.
 
M

Marc Gravell

I did some more playing with this, and it looks like (irritatingly) in
the event of a hang, the Begin... approach is more stable... sorry to
confuse things! I'll see if I can get that working...

Marc
 
A

Alistair George

Marc said:
I did some more playing with this, and it looks like (irritatingly) in
the event of a hang, the Begin... approach is more stable... sorry to
confuse things! I'll see if I can get that working...

Marc
OK, but are you saying that the method can hang due to comms problems?
 
C

Chris Dunaway

OK, but are you saying that the method can hang due to comms problems?

Be careful when redirecting both standard output and standard error as
there can be a deadlock issue. Check the docs which give some more
details about this issue.

Chris
 
A

Alistair George

Chris said:
Be careful when redirecting both standard output and standard error as
there can be a deadlock issue. Check the docs which give some more
details about this issue.

Chris
Thanks Chris.
Mark did you want to go any further or shall I proceed with caution
using the code that you promulgated?
Cheers,
Alistair.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top