How can a process be blocking another process (that it spawned)? - c#

I have written a utility program that is used to start and stop a specific process. Now, in testing it, it somehow seems to be blocking the process that it spawns!
It uses named system events (see System.Threading.EventWaitHandle). After starting the process, it waits for the event to be set:
private static int StartRavenDB(string fileName, string workingDirectory, string arguments)
{
var process = new Process
{
StartInfo =
{
FileName = fileName,
WorkingDirectory = workingDirectory,
Arguments = arguments,
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardInput = true,
RedirectStandardOutput = true
}
};
process.Start();
var eventWaitHandle = new EventWaitHandle(false, EventResetMode.ManualReset, "StartStopRavenDBUtility");
eventWaitHandle.Reset();
eventWaitHandle.WaitOne();
process.StandardInput.WriteLine("q");
process.WaitForExit();
return process.ExitCode;
}
Now, the RavenDB process that starts is a web server listening on localhost:8080.
Shortly after starting that process using the above utility, the process does not respond to web requests. It keeps timing out. As soon as I kill the utility process, everything starts to work normal.
For the record, I'm 100% the EventWaitHandle is not set yet - the RavenDB process is there, but it doesn't behave as it should.
I don't know what is happening or why, it's a completely separate process. What causes this problem?

You should subscribe to the OutputDataReceived event or at least read the redirected standard output, to avoid blocking the thread. From the documentation:
These dependencies can cause deadlock conditions. When the caller
reads from the redirected stream of a child process, it is dependent
on the child. The caller waits for the read operation until the child
writes to the stream or closes the stream. When the child process
writes enough data to fill its redirected stream, it is dependent on
the parent. The child process waits for the next write operation until
the parent reads from the full stream or closes the stream. The
deadlock condition results when the caller and child process wait for
each other to complete an operation, and neither can continue. You can
avoid deadlocks by evaluating dependencies between the caller and
child process.

Related

Batch script calling robocopy in Process won't terminate

If process.Kill() is called from another thread or even another program, the process never comes out of WaitForExit() if the batch script used robocopy.exe until it is finished as if it wasn't killed.
Robocopy.exe is called from the batch script. Every other script or program ends as you'd expect.
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = "batch.bat";
startInfo.UseShellExecute = false;
startInfo.CreateNoWindow = true;
startInfo.RedirectStandardOutput = true;
startInfo.OutputDataReceived += CaptureHandler;
startInfo.RedirectStandardError = true;
startInfo.ErrorDataReceived += CaptureHandler;
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
The batch script looks like:
#echo off
call "robocopy.exe" "somedir" "somedest" /mir /fp /ndl /njh /njs /ns
I have a feeling it has to do with the output handlers.
I tried using process.CancelErrorRead and process.CancelOutputRead() as well after the Kill() call and before, no luck.
Oddly, if you use process.WaitForExit(timeout) overload, it will return true immediately after Kill() from the other thread. However, it's lying. The process is still running! If you try process.WaitForExit() again, as per the MSDN doc, it will still wait for the process to finish despite HasExited saying true.
To ensure that asynchronous event handling has been completed, call the WaitForExit() overload that takes no parameter after receiving a true from this overload.
https://msdn.microsoft.com/en-us/library/ty0d8k56(v=vs.110).aspx
You are successfully killing the batch processor (cmd.exe) but doing so won't kill robocopy, which is a separate process.
It doesn't seem to be documented, but when we look at the .NET source code it turns out that the Process.WaitForExit() method doesn't just wait for the process to exit, it also waits for end-of-file on the standard output and standard error streams. In this scenario, that means that it waits for robocopy to finish even after the batch processor has been killed.
(The overload of Process.WaitForExit with a timeout does not have this extra logic.)
I think this constitutes a bug in the .NET framework. At the very least, it should be documented.
As a workaround, you can use .HasExited and/or the version of WaitForExit with a timeout to determine whether the process has exited or not. Of course, in your scenario you might prefer to wait for grandchild processes, in which case your code is already behaving as desired.
I ran into the same problem. In my case, dropping the /mt switch from the RoboCopy argument list seemed to fix the issue.
Having followed up on Harry Johnston's helpful answer, I found that the process completes normally when you avoid RedirectStandardOutput = true. If this isn't an acceptable solution I found that using robocopy's /LOG:"C:\logs\robocopy.txt" switch to send its standard output to an external log file also works (although you lose the ability to get the file/directory log output from the process object itself).
Looks like right now the only way to do this without the application knowing to terminate Robocopy.exe specifically is to do kill the children of the script process before killing the script itself:
Kill process tree programmatically in C#
/// <summary>
/// Kill a process, and all of its children, grandchildren, etc.
/// </summary>
/// <param name="pid">Process ID.</param>
private static void KillProcessAndChildren(int pid)
{
ManagementObjectSearcher searcher = new ManagementObjectSearcher
("Select * From Win32_Process Where ParentProcessID=" + pid);
ManagementObjectCollection moc = searcher.Get();
foreach (ManagementObject mo in moc)
{
KillProcessAndChildren(Convert.ToInt32(mo["ProcessID"]));
}
try
{
Process proc = Process.GetProcessById(pid);
proc.Kill();
}
catch (ArgumentException)
{
// Process already exited.
}
}

Process.WaitForExit doesn't return even though Process.HasExited is true

I use Process.Start to start a batch file. The batch file uses the "START" command to start several programs in parallel and then exits.
Once the batch file is done Process.HasExited becomes true and Process.ExitCode contains the correct exit code.
But when I call Process.WaitForExit() it hangs / never returns.
The following piece of code demonstrates the problem. It creates a batch file, starts it and then prints:
Process is still running...
Batch file is done!
Process has exited. Exit code: 123
Calling WaitForExit()...
It should then print:
WaitForExit returned.
... but it never does (even though HasExited is true and we already have an ExitCode).
open System.IO
open System.Diagnostics
open System.Threading
let foobat = """
START ping -t localhost
START ping -t google.com
ECHO Batch file is done!
EXIT /B 123
"""
File.WriteAllText("foo.bat", foobat)
use p = new Process(StartInfo = ProcessStartInfo("foo.bat",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true))
let onOutput = DataReceivedEventHandler(fun _ args -> printfn "%s" args.Data)
p.OutputDataReceived.AddHandler onOutput
p.ErrorDataReceived.AddHandler onOutput
p.Start() |> ignore
p.BeginErrorReadLine()
p.BeginOutputReadLine()
while not p.HasExited do
printfn "Process is still running..."
Thread.Sleep(1000)
printfn "Process has exited. Exit code: %d" p.ExitCode
printfn "Calling WaitForExit()..."
p.WaitForExit()|> ignore
printfn "WaitForExit returned."
I noticed that this only happens when the batch file contains "START" commands and when standard output and/or standard error are redirected.
Why does WaitForExit() never return?
What's the right way to wait for such a process to exit?
Is it safe to just poll Process.HasExited or can that result in other problems?
PS.: I just noticed that calling WaitForExit(100000) with a huge timeout (that definitely doesn't expire) returns immediately when the process exits. Wierd. Without timeout it hangs.
This seems to be an artifact (I'd say "bug") in the specific implementation of the event-based asynchronous handling of StandardOutput and StandardError.
I noticed that while I was able to easily reproduce your problem, simply by running the code you provided (excellent code example, by the way! :) ), the process did not actually hang indefinitely. Rather, it returned from WaitForExit() once both of the child processes that had been started had themselves exited.
This seems to be an intentional part of the implementation of the Process class. In particular, in the Process.WaitForExit() method, once it has finished waiting on the process handle itself, it checks to see if a reader for either stdout or stderr has been created; if so, and if the timeout value for the WaitForExit() call is "infinite" (i.e. -1), the code actually waits for the end-of-stream on the reader(s).
Each respective reader is created only when the BeginOutputReadLine() or BeginErrorReadLine() method is called. The stdout and stderr streams are themselves not closed until the child processes have closed. So waiting on the end of those streams will block until that happens.
That WaitForExit() should behave differently depending on whether one has called either of the methods that start the event-based reading of the streams or not, and especially given that reading those streams directly does not cause WaitForExit() to behave that way, creates an inconsistency in the API that makes it much more difficult to understand and use. While I'd personally call this a bug, I suppose it's possible that the implementor(s) of the Process class are aware of this inconsistency and created it on purpose.
In any case, the work-around would be to read StandardOutput and StandardError directly instead of using the event-based part of the API. (Though of course, if one's code were to wait on those streams, one would see the same blocking behavior until the child processes close.)
For example (C#, because I don't know F# well enough to slap a code example like this together quickly :) ):
using System;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
namespace TestSO26713374WaitForExit
{
class Program
{
static void Main(string[] args)
{
string foobat =
#"START ping -t localhost
START ping -t google.com
ECHO Batch file is done!
EXIT /B 123
";
File.WriteAllText("foo.bat", foobat);
Process p = new Process { StartInfo =
new ProcessStartInfo("foo.bat")
{
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true
} };
p.Start();
var _ = ConsumeReader(p.StandardOutput);
_ = ConsumeReader(p.StandardError);
Console.WriteLine("Calling WaitForExit()...");
p.WaitForExit();
Console.WriteLine("Process has exited. Exit code: {0}", p.ExitCode);
Console.WriteLine("WaitForExit returned.");
}
async static Task ConsumeReader(TextReader reader)
{
string text;
while ((text = await reader.ReadLineAsync()) != null)
{
Console.WriteLine(text);
}
}
}
}
Hopefully the above work-around or something similar will address the basic issue you've run into. My thanks to commenter Niels Vorgaard Christensen for directing me to the problematic lines in the WaitForExit() method, so that I could improve this answer.

Killing external process when application dies

I've been working on a small piece that calls an external executable (ffmpeg in my case)
And then I wrote a test and used test runner in a debug mode, now if I stop debugging (terminate) it still runs ffmpeg. I tried to kill the process in the finalizer and with IDisposable - it still runs. How can I make sure that the process never will be left like that, and if the caller dies or fails or gets stopped by any means, the ffmpeg executable guaranteed to be killed.
I run the process as usual (nothing fancy)
var processInfo = new ProcessStartInfo(ffmpegPath)
{
UseShellExecute = false,
Arguments = arguments,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
};
using (var ffmpegProc = new Process())
{
ffmpegProc.EnableRaisingEvents = true;
ffmpegProc.StartInfo = processInfo;
ffmpegProc.Start();
ffmpegProc.WaitForExit();
}
You should use JobObject
With JobObject you can add child processes. So if the main process is killed or closed the os will terminate all child processes.
source: http://www.xtremevbtalk.com/showpost.php?p=1335552&postcount=22
Another solution is to pass to child object the parent PID.
Any child have to check for parent PID existence and if not found kill itself
There is nothing you can do when your process is terminated. There is no code run in your process after someone kills it with TerminateProcess call.
In case of more graceful cases (i.e. unhandled exception) you may have global handler that does something close to what you want.

Avoid locking service if process crashes / hangs

I am currently developing a windows service which implements fileSystemWatcher. Videos are uploaded into a folder at which point the filewatcher fires the created event as below to convert the video.
private void fileSystemWatcher_Created(object sender, System.IO.FileSystemEventArgs e)
{
if (ConvertVideo(e.FullPath, e.Name))
{
WriteToEventLog(String.Format("Successfully converted video - {0}", e.FullPath), EventLogEntryType.Information);
}
}
Within ConvertVideo a new process is created but I have run into issues where the process crashes / hangs / disappears and it appears the main thread is then locked as its waiting for WaitForExit() which effectively crashes the service as no other videos can then be converted. How could I avoid locking the entire service if the process dies?
private bool ConvertVideo(string SourcePath, string Filename)
{
try
{
// Create new process
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.CreateNoWindow = false;
startInfo.UseShellExecute = false;
startInfo.FileName = "C:\Handbrake\HandBrakeCLI.exe";
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.Arguments = GetArguments(SourcePath, Filename);
int? exitCode = null;
using (Process exeProcess = Process.Start(startInfo))
{
exeProcess.WaitForExit();
exitCode = exeProcess.ExitCode;
}
}
catch(Exception ex)
{
return false;
}
}
NOTE: Code is shortened for this example
According to MSDN, Process.WaitForExit should return if your process crashes (emphasis added):
When an associated process exits (that is, when it is shut down by the
operation system through a normal or abnormal termination), the system
stores administrative information about the process and returns to the
component that had called WaitForExit().
It appears that your HandBrake process is just hanging and staying alive. The best solution would be to debug that process and figure out where it is crashing, but not closing down. Do you have access to the HandBrakeCLI.exe code?
If you don't have access to the HandBrake.exe code: you could use Process.WaitForExit(Int32) to set a timeout. If the timeout is reached, you may want to manually kill your process via the Process.Kill function, or all subsequent calls to Process.Start(ProcessStartInfo) will not work properly, since they will only return a new process if the process wasn'talready running:
A new Process component that is associated with the process resource,
or null if no process resource is started (for example, if an existing
process is reused).
1)
You should spawn your process and wait for it to terminate in a separate thread, in order to avoid blocking your main thread.
2)
You could use the WaitForExit method that takes the max time to wait for the process as a parameter. You'll then be able to avoid the case a thread of your program is blocked forever.

Console application prompting for input

I'm trying to put together a wrapper around a console application using StandardInput and StandardOutput. I'm getting stuck where the console application would prompt for input such as a password.
I'd like to read from StandardOutput, prompt the user using the read text, and write the user's input back to the console application using its StandardInput. Seems easy enough, here's what I have currently:
Process process = new Process()
{
StartInfo =
{
FileName = "bin\\vpnc.exe",
Arguments = "--no-detach --debug 0",
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardInput = true,
RedirectStandardOutput = true,
}
};
process.OutputDataReceived += (s, args) =>
{
textBlock1.Dispatcher.Invoke(new Action(() =>
{
textBlock1.Text += args.Data;
}));
};
process.Start();
process.BeginOutputReadLine();
The problem is that BeginOutputReadLine() is doing just that...waiting for a line ending. In this case it just sits, and sits, and sits because there is no line to read...the console application has written out text with no line ending and is waiting for input. Coincidentally, when I manually kill the process the event fires and I get the text.
Is there a way to tell that the process is waiting for StandardInput? Or am I missing a completely obvious way to accomplish the goal?
Unless you need something asynchronous you probably want ReadToEnd.
Here is a list of all StreamReader Methods
process.StandardOutput.BaseStream.BeginRead(...) is a potential substitute for your readline, and that will not wait for a line ending however you'd need to know what terminates the output to figure out when not to start wait for the next chunk of data
As Rune said, you can access directly to the output stream of the process (process.StandardOutput) and read from there (if you don't want to wait until a line break is entered into the console app), but this means that you need to check periodically for new data.
To interact with the application, you can just write to the StandardInput of the process (create a StreamWriter that writes to the process.StandardInput).
A nice sample of writing to it is on the MSDN documentation (http://msdn.microsoft.com/en-us/library/system.diagnostics.process.beginoutputreadline.aspx).
Hope this helps
You need to use the synchronous read method and handle any necessary threading yourself. The below code won't tell you that input is expected, but you will be able to detect that a prompt is displayed.
char[] b = new char[1024];
while (!process.HasExited) {
int c = process.StandardOutput.Read(b, 0, b.Length);
context.Response.Write(b, 0, c);
Thread.Sleep(100);
}

Categories