In my WPF application, I starts new process which runs batch file.
code is as follows
public void startProcess(string batchFileName)
{
Process proc = new Process();
//command to execute
proc.StartInfo.FileName = batchFileName;
proc.StartInfo.UseShellExecute = false;
// set up output redirection
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.RedirectStandardError = true;
proc.EnableRaisingEvents = true;
proc.StartInfo.CreateNoWindow = true;
// see below handler
proc.ErrorDataReceived += proc_DataReceived;
proc.OutputDataReceived += proc_DataReceived;
//start a process
proc.Start();
proc.BeginErrorReadLine();
proc.BeginOutputReadLine();
waitThread = new Thread(new ThreadStart(WaitForProcess));
waitThread.Start();
}
void proc_DataReceived(object sender, DataReceivedEventArgs e)
{
// output will be in string e.Data
if (e.Data != null)
{
logsTextBox.Dispatcher.BeginInvoke(new SetLogText(UpdateText), DispatcherPriority.Normal, e.Data);
}
}
private void WaitForProcess()
{
proc.WaitForExit();
proc.Close();
}
Now I want to stop this newly created process in between. How to achieve that?
Any suggestions/pointers.
How to stop a running process
Close method can be used to stop a process
proc.Close();
CloseMainWindow can be used to request a stop
proc.CloseMainWindow();
Kill can be used to destroy the process
proc.Kill();
Remarks
Kill forces a termination of the process, while CloseMainWindow only requests a termination. When a process with a graphical interface is executing, its message loop is in a wait state. The message loop executes every time a Windows message is sent to the process by the operating system. Calling CloseMainWindow sends a request to close to the main window, which, in a well-formed application, closes child windows and revokes all running message loops for the application. The request to exit the process by calling CloseMainWindow does not force the application to quit. The application can ask for user verification before quitting, or it can refuse to quit. To force the application to quit, use the Kill method. The behavior of CloseMainWindow is identical to that of a user closing an application's main window using the system menu. Therefore, the request to exit the process by closing the main window does not force the application to quit immediately.
more info here
http://msdn.microsoft.com/en-us/library/vstudio/system.diagnostics.process.close
http://msdn.microsoft.com/en-us/library/vstudio/system.diagnostics.process.closemainwindow
http://msdn.microsoft.com/en-us/library/vstudio/system.diagnostics.process.kill
Related
I've a method that contains a process that must be stopped in a deadline( ex: 3 seconds) whether it has finished or not, and I don't want to wait if it has finished executing before reaching that dead line.
using Process.WaitForExit(3000) makes the program wait 3s even if the process has stopped before reaching the limit.
One more thing, I'm using process.StandardOutput.ReadToEnd(); to read the execution result, I don't care if it returns null or empty string or whatever if it doesn't finish.
And I guess that timers will cause the same problem.
Any Ideas?
Exited event of your process can be handled for detecting exit time.
WaitForExit returns a Boolean value that indicates your process has reached the timeout before exit or not.
Test this code:
Process proc = new Process();
ProcessStartInfo procInfo = new ProcessStartInfo()
{
FileName = "d:/test.exe",
UseShellExecute = false,
RedirectStandardOutput = true
};
proc.StartInfo = procInfo;
proc.EnableRaisingEvents = true;
proc.Exited += (o, args) =>
{
MessageBox.Show(proc.StandardOutput.ReadToEnd());
};
proc.Start();
if (proc.WaitForExit(3000))
{
MessageBox.Show("YES");
}
else
{
MessageBox.Show("NO");
}
I use ProcessStartInfo to run a console aplication and ProcessStartInfo can read text from the console after the console is closed:
using (Process p = Process.Start(st))
{
//Thread.Sleep(2000);
p.WaitForExit();
using (StreamReader rd = p.StandardOutput)
{
result = rd.ReadToEnd();
p.Close();
String result1 = String.Copy(result);
}
Is there another method to read text from the console while it is open?
You can use the OutputDataReceived of the Process class
string result = string.Empty;
using (Process process = new Process())
{
process.StartInfo = st; // your ProcessStartInfo
StringBuilder resultBuilder = new StringBuilder();
process.OutputDataReceived += (sender, e) =>
{
st.AppendLine(e.Data);
};
process.Start();
process.BeginOutputReadLine();
process.WaitForExit();
result = resultBuilder.ToString();
}
You simply add an event handler to the Process's OutputDataReceived event that gets called whenever the process outputs a line.
Then you need to call BeginOutputReadLine() after the process has been started to begin receiving those events.
In this example, I still wait for the process to exit just to complete the code. Of course you don't need to wait, the events occure while the process is running. So you can store your process variable in a member and dispose it later or even subscribe to its Exited event to get informed when the process terminates.
I want to ensure that my logic is correct here. I want to run a process for timeout seconds, if it runs for longer it should be immediately killed.
The completed flag should reliably indicate whether the process completed as intended, e.g was not killed, and did not crash or throw an exception.
Also, I am not positive if the check to process.HasExited is correct. If process.WaitForExit() returns false and Kill() succeeds, then will process.HasExited always be true? That would be my assumption but I wanted to confirm. Also, what if anything can be done if Kill() fails,
besides just logging?
using (process = new Process())
{
process.EnableRaisingEvents = true;
process.OutputDataReceived += new DataReceivedEventHandler(OnOutputDataReceived);
process.ErrorDataReceived += new DataReceivedEventHandler(OnErrorDataReceived);
process.Exited += new EventHandler(OnExited);
process.StartInfo = startInfo;
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
if (!process.WaitForExit(timeout))
{
try
{
process.Kill();
}
catch (Exception e)
{
LogError(e, MethodBase.GetCurrentMethod());
}
finally
{
this.completed = false;
}
}
else
{
if (process.HasExited)
{
this.code = process.ExitCode;
this.completed = true;
}
else
{
this.completed = false;
}
}
}
Yes, the HasExited will always be true in your case.
According to MSDN,
"A value of true for HasExited indicates that the associated process has terminated, either normally or abnormally.[...]A process can terminate independently of your code. If you started the process using this component, the system updates the value of HasExited automatically, even if the associated process exits independently."
However, if your process crashes and terminates before your timeout, then your code will set it as completed anyway. Maybe you should check the exit code, but it can have different meanings for each process:
if (process.ExitCode != 0)
{
this.completed = false;
}
For crashes, there are some approaches here and here, but generally you can't detect crashes for all processes.
We use the following in a .net console app
private void InitTimer()
{
double lInterval = Convert.ToDouble(AppSettings("MaxExecutionTime"));
lInterval = lInterval * 60 * 1000;
tm = new System.Timers.Timer(lInterval); // global timer object
tm.Elapsed += OnTimedEvent;
tm.Enabled = true;
}
public void ThreadProc(object stateinfo)
{
// set error code here
Environment.Exit(0);
}
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
Threading.ThreadPool.QueueUserWorkItem(new Threading.WaitCallback(ThreadProc));
}
In C, you could set an OS alarm using the alarm function. When it expired, a SIGALRM would be sent to your process, which kills it if no handler is set.
You can use this. It is a C# wrapper over the JobObjects functionallity.
The idea behind is (low level outline that is embedded inside the library I mentioned):
Create a job object.
Configure the job object to have a time limit of x seconds.
Create a process and before resuming it assing it to the job object.
Resume the process.
The process will be killed by the operating system when the time passes. YOu usually get notified by a non zero return code, or a callback. The JobObject API itself allows callbacks, not sure about the C# wrapper.
Also using job objects you can restrict memory usage.
On the page I mentioned you can find examples also.
UPDATE
After I wrote the above statements I have found this Kill child process when parent process is killed. They use the JobObjects for another task, but the usage of JobObjects should be the same as for your case.
I am using ffmpeg.exe to convert video files to flv format. For that purpose i use a windows service to run the conversion process in background. While trying to convert large files(i experienced it when the file size is >14MB) through windows service it gets stuck at the line which starts the process(ie, process.start();).
But when i tried to execute ffmpeg.exe directly from command prompt it worked with out any problems.
My code in windows service is as follows:
private Thread WorkerThread;
protected override void OnStart(string[] args)
{
WorkerThread = new Thread(new ThreadStart(StartHandlingVideo));
WorkerThread.Start();
}
protected override void OnStop()
{
WorkerThread.Abort();
}
private void StartHandlingVideo()
{
FilArgs = string.Format("-i {0} -ar 22050 -qscale 1 {1}", InputFile, OutputFile);
Process proc;
proc = new Process();
try
{
proc.StartInfo.FileName = spath + "\\ffmpeg\\ffmpeg.exe";
proc.StartInfo.Arguments = FilArgs;
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.CreateNoWindow = false;
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.RedirectStandardError = true;
eventLog1.WriteEntry("Going to start process of convertion");
proc.Start();
string StdOutVideo = proc.StandardOutput.ReadToEnd();
string StdErrVideo = proc.StandardError.ReadToEnd();
eventLog1.WriteEntry("Convertion Successful");
eventLog1.WriteEntry(StdErrVideo);
}
catch (Exception ex)
{
eventLog1.WriteEntry("Convertion Failed");
eventLog1.WriteEntry(ex.ToString());
}
finally
{
proc.WaitForExit();
proc.Close();
}
How can I get rid of this situation.
It seems you caught a deadlock because you performed a synchronous read to the end of both redirected streams.
A reference from MSDN:
There is a similar issue when you read
all text from both the standard output
and standard error streams. The
following C# code, for example,
performs a read operation on both
streams.
// Do not perform a synchronous read to the end of both
// redirected streams.
// string output = p.StandardOutput.ReadToEnd();
// string error = p.StandardError.ReadToEnd();
// p.WaitForExit();
// Use asynchronous read operations on at least one of the streams.
p.BeginOutputReadLine();
string error = p.StandardError.ReadToEnd();
p.WaitForExit();
The code example avoids the deadlock
condition by performing asynchronous
read operations on the StandardOutput
stream. A deadlock condition results
if the parent process calls
p.StandardOutput.ReadToEnd followed by
p.StandardError.ReadToEnd and the
child process writes enough text to
fill its error stream. The parent
process would wait indefinitely for
the child process to close its
StandardOutput stream. The child
process would wait indefinitely for
the parent to read from the full
StandardError stream.
You can use asynchronous read
operations to avoid these dependencies
and their deadlock potential.
Alternately, you can avoid the
deadlock condition by creating two
threads and reading the output of each
stream on a separate thread.
In my C# program I call an external program from command line using a process and redirecting the standard input. After I issue the command to the external program, every 3 seconds I have to issue another command to check the status - the program will respond with InProgress or Finished. I would like some help in doing this as efficient as possible. The process checks the status of a long running operation executed by a windows service (for reasons I would not like to detail I cannot interact directly with the service), therefore after each command the process exits, but the exitcode is always the same no matter the outcome.
use Process.Exited event and Process.ExitCode property
For examples:
http://msdn.microsoft.com/en-us/library/system.diagnostics.process.exited.aspx
http://msdn.microsoft.com/en-us/library/system.diagnostics.process.exitcode.aspx
You can use a Timer to execute some code at a specified interval, and Process.StandardOutput to read the output of the process:
Timer timer = new Timer(_ =>
{
Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = "foobar.exe";
p.Start();
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
switch (output)
{
case "InProgress":
// ...
break;
case "Finished":
// ...
break;
}
});
timer.Change(TimeSpan.FromSeconds(0), TimeSpan.FromSeconds(3));