Actually, I have the "Quick Console" turned off, so that's not it either. The difference is that Program A is a .Net app, whereas Program B is not. Sounds like another bug report, since nothing in the docs specifies that the process app has to be a .Net app in order for it to work. However, I wouldn't keep my fingers crossed. The documentation states "Occurs when an application writes to its redirected StandardOutput stream". A non-.Net app doesn't have a StandardOutput stream, at least not the classStandardOutput.
This is from a larger project so I've tried to cut and paste the relevant bits. This took ages of trial and error to get working.
Hope you can make sense of it.
Cheers Steve
The process is started from a class that also has a Windows form (to show progress). The sendclip method starts the commandline process which returns as soon as the process is started - as then events take over. The show dialog displays the form and kind of forces a 'wait'. I use events to close the form when the process finishes and things carry on
this.sendclip(clip, clip.Partialcommandline, Qsystem); //Showing the dialog modally forces a wait until the dialog closes if (!Main.batchonly) this.ShowDialog();
Here's the sendclip method //method to setup and start a transfer of a clip //set up process instance and startinformation //redirect console output //This method only starts things off - events handle the rest public void sendclip(MediaClip clip, string commandline, string Qsystem) {
//some irrelevant code removed
if (!Main.batchonly) { //setup the xmlput process Process xmlput = new Process(); ProcessStartInfo xmlputstartinfo = new ProcessStartInfo ((string)Registry.GetValue(Main.key1, "UtilDir", "Not found") + @"\xmlput.exe");
// Saves a bit of typing as it is repeated in other places Main.xmlutilsetup(xmlput, xmlputstartinfo, arguments);
//set up and show transfer status headlines, calculate progress bar increment la_ProcessStatus.Text = "Sending Clip " + clip.Sourcedir + " to " + Qsystem; la_clargs.Text = "xmlput " + arguments; pb_clip.Value = 1; pb_clip.Minimum = 1; pb_clip.Maximum = int.Parse(clip.Duration) + 1;
//set up event handlers for the process end and output xmlput.EnableRaisingEvents = true; xmlput.OutputDataReceived += new DataReceivedEventHandler(xmlput_OutputDataReceived); xmlput.Exited += new EventHandler(xmlput_Exited);
// run the process and start watching for output xmlput.Start(); xmlput.BeginOutputReadLine(); } }
Here's all the event handlers
//Event handler for process exit - simply close form //BeginInvoke avoids cross thread exceptions void xmlput_Exited(object sender, EventArgs e) { this.BeginInvoke(new labelupdate(closeform)); }
//actual method called by above delegate when the process has ended public void closeform() { this.Close(); }
//Event handler for redirected process output //Asynchronously gets lines of output from Qxmlput //Begin.Invoke avoids cross thread exceptions on label and progress bar update private void xmlput_OutputDataReceived(object sender, DataReceivedEventArgs e) { if(!String.IsNullOrEmpty(e.Data)) { processoutput = e.Data;
//look for the framenum line ignore everything else //when found it means another frame has been transferred //so update the progress bar and label //begininvoke required to avoid cross thread errors if (processoutput.Contains("<FRA")) { processmessage = processoutput; la_ProcessComms.BeginInvoke(new labelupdate(updatecomms)); } else if (processoutput.StartsWith("<ERROR>")) { TransferFailed = true; } } }
// Actual method called by above delegate to update form with process output public void updatecomms() { la_ProcessComms.Text = processmessage; if (pb_clip.Value < pb_clip.Maximum) { pb_clip.Value ++; } }
//delegate to avoid cross thread errors //used to update labels and progress bar from process events public delegate void labelupdate();
//setup processes to redirect output, avoid flashing and with CL arguments //static public so can be used by other classes public static void xmlutilsetup(Process xmlutil, ProcessStartInfo xmlutilstartinfo, string xmlutilargs) { xmlutil.StartInfo = xmlutilstartinfo; xmlutilstartinfo.RedirectStandardOutput = true; xmlutilstartinfo.UseShellExecute = false; xmlutilstartinfo.CreateNoWindow = true; xmlutilstartinfo.Arguments = xmlutilargs; }
"Documentation" for the app above. Compiled as Test.exe.
When executed with no parameters, it spawns a process and executes itself with a parameter. I then captures the output of the spawned process and writes it to the Console.
When executed with at least one parameter, it loops ten times spitting "this is a test" out to the Console once a second.
I duplicated your Process.WaitForExit( int) problem, probably should report that as a bug. I didn't have a problem with the event handler though. I used a Console app to test and it worked just fine. My guess (just a guess) is that your GUI thread is blocked waiting on something (the Thread to finish ) such that it can't respond to the events.
I have something similar working in c# express. A process runs a command line utility and I catch the redirected output.
I'm quite new to all this so can't diagnose your code but I don't use any explicit threads - the process seems to run on its own thread quite happily anyway.
One line I have which seems not to be there in the examples shown is: xmlput.EnableRaisingEvents = true; (xmlput is my process)
The other difference is that once I've kicked things off I do no more and let events take over. I remember trying waits and sleeps and getting absolutely nowhere.
There's not much to it. Note that if you hit the Enter key before the process is completed, the thread.Abort() gets called, but the ProcessRunner thread doesn't get interrupted until the process is complete.
class Program { public static void Main(string [] args) { try { if (args.Length > 0) { for (int i = 0; i < 10; i++) { Console.WriteLine("This is a test"); Thread.Sleep(1000); } } else { ProcessRunner pr = new ProcessRunner(); Thread thread = new Thread(new ThreadStart(pr.Go)); thread.Start(); Console.ReadLine(); thread.Abort(); thread.Join(); } } catch (Exception e) { Console.WriteLine("Unexpected Exception:" + e.ToString()); } } }
Process & WaitForExit & OutputDataReceived
darwind
Mike Houglum
.
Dolfandave
Yaroslav58211
Nick Foster
.
JeffreySax
.
rhoule
RoMoVi
cisco0407
This is from a larger project so I've tried to cut and paste the relevant bits. This took ages of trial and error to get working.
Hope you can make sense of it.
Cheers
Steve
The process is started from a class that also has a Windows form (to show progress). The sendclip method starts the commandline process which returns as soon as the process is started - as then events take over. The show dialog displays the form and kind of forces a 'wait'. I use events to close the form when the process finishes and things carry on
this.sendclip(clip, clip.Partialcommandline, Qsystem);
//Showing the dialog modally forces a wait until the dialog closes
if (!Main.batchonly) this.ShowDialog();
Here's the sendclip method
//method to setup and start a transfer of a clip
//set up process instance and startinformation
//redirect console output
//This method only starts things off - events handle the rest
public void sendclip(MediaClip clip, string commandline, string Qsystem)
{
//some irrelevant code removed
if (!Main.batchonly)
{
//setup the xmlput process
Process xmlput = new Process();
ProcessStartInfo xmlputstartinfo = new ProcessStartInfo
((string)Registry.GetValue(Main.key1, "UtilDir", "Not found") + @"\xmlput.exe");
// Saves a bit of typing as it is repeated in other places
Main.xmlutilsetup(xmlput, xmlputstartinfo, arguments);
//set up and show transfer status headlines, calculate progress bar increment
la_ProcessStatus.Text = "Sending Clip " + clip.Sourcedir + " to " + Qsystem;
la_clargs.Text = "xmlput " + arguments;
pb_clip.Value = 1;
pb_clip.Minimum = 1;
pb_clip.Maximum = int.Parse(clip.Duration) + 1;
//set up event handlers for the process end and output
xmlput.EnableRaisingEvents = true;
xmlput.OutputDataReceived += new DataReceivedEventHandler(xmlput_OutputDataReceived);
xmlput.Exited += new EventHandler(xmlput_Exited);
// run the process and start watching for output
xmlput.Start();
xmlput.BeginOutputReadLine();
}
}
Here's all the event handlers
//Event handler for process exit - simply close form
//BeginInvoke avoids cross thread exceptions
void xmlput_Exited(object sender, EventArgs e)
{
this.BeginInvoke(new labelupdate(closeform));
}
//actual method called by above delegate when the process has ended
public void closeform()
{
this.Close();
}
//Event handler for redirected process output
//Asynchronously gets lines of output from Qxmlput
//Begin.Invoke avoids cross thread exceptions on label and progress bar update
private void xmlput_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
if(!String.IsNullOrEmpty(e.Data))
{
processoutput = e.Data;
//look for the framenum line ignore everything else
//when found it means another frame has been transferred
//so update the progress bar and label
//begininvoke required to avoid cross thread errors
if (processoutput.Contains("<FRA"))
{
processmessage = processoutput;
la_ProcessComms.BeginInvoke(new labelupdate(updatecomms));
}
else if (processoutput.StartsWith("<ERROR>"))
{
TransferFailed = true;
}
}
}
// Actual method called by above delegate to update form with process output
public void updatecomms()
{
la_ProcessComms.Text = processmessage;
if (pb_clip.Value < pb_clip.Maximum)
{
pb_clip.Value ++;
}
}
//delegate to avoid cross thread errors
//used to update labels and progress bar from process events
public delegate void labelupdate();
//setup processes to redirect output, avoid flashing and with CL arguments
//static public so can be used by other classes
public static void xmlutilsetup(Process xmlutil, ProcessStartInfo xmlutilstartinfo, string xmlutilargs)
{
xmlutil.StartInfo = xmlutilstartinfo;
xmlutilstartinfo.RedirectStandardOutput = true;
xmlutilstartinfo.UseShellExecute = false;
xmlutilstartinfo.CreateNoWindow = true;
xmlutilstartinfo.Arguments = xmlutilargs;
}
JainMohit
When executed with no parameters, it spawns a process and executes itself with a parameter. I then captures the output of the spawned process and writes it to the Console.
When executed with at least one parameter, it loops ten times spitting "this is a test" out to the Console once a second.
balance
Flinders
Maybe it blocks because you're frying the processor A MessageBox every millisecond is a bit extreme! Try:
while (!p.WaitForExit(2500)) {
Gabriele38
I have something similar working in c# express. A process runs a command line utility and I catch the redirected output.
I'm quite new to all this so can't diagnose your code but I don't use any explicit threads - the process seems to run on its own thread quite happily anyway.
One line I have which seems not to be there in the examples shown is:
xmlput.EnableRaisingEvents = true; (xmlput is my process)
The other difference is that once I've kicked things off I do no more and let events take over.
I remember trying waits and sleeps and getting absolutely nowhere.
Let me know if you'd like the whole thing posted.
Cheers
Steve
Morten Hvidberg-Knudsen
There's not much to it. Note that if you hit the Enter key before the process is completed, the thread.Abort() gets called, but the ProcessRunner thread doesn't get interrupted until the process is complete.
class Program
{
public static void Main(string [] args)
{
try
{
if (args.Length > 0)
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine("This is a test");
Thread.Sleep(1000);
}
}
else
{
ProcessRunner pr = new ProcessRunner();
Thread thread = new Thread(new ThreadStart(pr.Go));
thread.Start();
Console.ReadLine();
thread.Abort();
thread.Join();
}
}
catch (Exception e)
{
Console.WriteLine("Unexpected Exception:" + e.ToString());
}
}
}
class ProcessRunner
{
private void OnTick( object obj,DataReceivedEventArgs args)
{
System.Console.WriteLine("Received: {0}", args.Data);
System.Console.WriteLine(Thread.CurrentThread.Name);
}
public string Cmd
{
get { return cmd; }
set { cmd = value; }
}
public void Go()
{
try
{
System.Console.WriteLine("Started...");
proc = new Process();
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.RedirectStandardOutput = true;
proc.OutputDataReceived += new DataReceivedEventHandler(OnTick);
proc.StartInfo.FileName = @"Test.exe";
proc.StartInfo.Arguments = @"someargs";
proc.StartInfo.CreateNoWindow = true;
proc.Start();
proc.BeginOutputReadLine();
proc.WaitForExit(200);
System.Console.WriteLine("WaitForExit returned");
Thread.Sleep(2000);
proc.Close();
}
catch (Exception)
{
Console.WriteLine("Exception caught, rethrowing");
Thread.Sleep(2000);
throw;
}
}
private string cmd;
private Process proc;
}