From the MSDN example of using stdoutput of newly created process:
// This is the code for the base process
Process myProcess = new Process();
// Start a new instance of this program but specify the 'spawned' version.
ProcessStartInfo myProcessStartInfo = new ProcessStartInfo(args[0], "spawn");
myProcessStartInfo.UseShellExecute = false;
myProcessStartInfo.RedirectStandardOutput = true;
myProcess.StartInfo = myProcessStartInfo;
myProcess.Start();
StreamReader myStreamReader = myProcess.StandardOutput;
// Read the standard output of the spawned process.
string myString = myStreamReader.ReadLine();
Console.WriteLine(myString);
myProcess.WaitForExit();
myProcess.Close();
If instead of myStreamReader.ReadLine() I'm using myStreamReader.ReadToEnd() shall I still use myProcess.WaitForExit()?
Or ReadToEnd() will wait until the process is finished?
EDIT:
Sorry for the diversion, to directly answer your question. Yes, you need to call Process.WaitForExit();. This will ensure that the process has yielded all its output before you call ReadToEnd()
ReadToEnd is synchronous function. Hence if you don't call it in your code, it will block your main thread until it captures only the first output from the StandardOutput, then that's it. But using WaitForExit will ensure that you have everything.
Also you might consider doing an asynchronous read of the process's output, see this MSDN Example that implements OutputDataRecieved
"ReadToEnd" is a function stored in "StreamReader" object and I don't think it has something to do with waiting for a process to exit, however the "Process" class might handle that itself. By the way, all the abilities "StreamReader" has are not useful in the situation you mentioned.
In my point of view, "WaitForExit" should be called and as you did "Close" too. Because they will release some system resources that no method else can. As far as I know, "ReadToEnd" method has nothing to do with calling those two.
Cheers
Related
Calling process.Dispose() or process.Close() (no matter which one, 'cos .Close is called inside .Dispose implementation) after process.Kill() sometimes hangs application.
I can't reproduce this bug stable, but sometimes when WaitForExit finishes by timeout passed to it, application hangs on process.Close() command.
Please suggest me, what the cause of this problem may be?
Note:
I've seen similar question. But there are not answers & upvoted comment says, that the cause of the problem perhaps at details, which are not provided at that question. So I added more details.
I've also seen a linked solution, but I can't use ProcessStartInfo.UseShellExecute = true; 'cos I need to redirect output.
Sorry for verbose code, but I pass it such way, 'cos at similar unanswered question commentators noticed, that not enough details provided (as I noted above)
private static async Task<int> RunMethod(string processArguments)
{
// 1. Prepare ProcessStartInfo
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.Arguments = processArguments;
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
startInfo.CreateNoWindow = true;
startInfo.UseShellExecute = false;
// 2. Create process inside using-block to be disposed
using (var proc = new Process())
{
proc.StartInfo = startInfo;
// 3. Subscribe output streams handlers
proc.OutputDataReceived += (sender, outputLine) => { HandleMessage(outputLine); };
proc.ErrorDataReceived += (sender, errorLine) => { HandleMessage(errorLine); };
// 4. Start process
if (!proc.Start())
{
proc.Close();
return -1;
}
// 5. Start the asynchronous read of the standard output stream.
proc.BeginOutputReadLine();
proc.BeginErrorReadLine();
// 6. Waiting process to exit with timeout on threadpool to not block UI thread
// Re#er warns me "captured variable `proc` is disposed in the outer scope". But I think it's Ok, 'cos we're awaiting this task inside using block (in next line)
var waitingProcessTask = Task.Run(() => proc.WaitForExit(TIMEOUT), _cancelToken);
bool hasExited = await waitingProcessTask;
// 7. Stop reading streams
// Not sure, these 2 CalncelXxxRead methods are needed. But hope it won't hurt at least
proc.CancelErrorRead();
proc.CancelOutputRead();
// 8. If !hasExited (i.e. TIMEOUT is reached) we kill the process
if (!hasExited)
{
Logger.Debug("0. Before Kill()");
proc.Kill();
proc.Refresh(); // not sure, it's needed
}
// If uncomment next 2 lines, then problem moves here from end of using block
//proc.Close();
//Logger.Debug("1. after .Close call"); // <------------------ This log we don't see sometimes
Logger.Debug("2. last inside using-block");
} // end of using block
Logger.Debug("3. after using-block"); // <------------------ This log we don't see sometimes (if `.Close` wasn't called before)
return 0;
}
I've figured out with my issue. The problem was: my process once in a while** spawned child daemon-process, which is ment to work forever. Child process (by design) always inherits redirected output streams if parent streams are redirected.
From the other side, .Kill() don't kill child processes - only for which it was kalled. (Until .Net 5.0, where .Kill(bool) solves this problem).
So .Dispose() or .Close() never finish, 'cos waits to release output streams, which are held by infinite child process.
It was very interesting adventure to figure out, what is happening =)
**) - that's why reproduce was very unstable
PS: Thank to #Blindy for directing me the way quite close to where the real cause of problem lie.
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.
}
}
Many examples and MSDN are using a new Process to get the exitcode, however, creat a new variable looks not so grace.So, I tried this
Process.Start("Application.exe", "parameter").WaitForExit().ExitCode
aimed to get the exitcode in one line but failed.
And is there any solution of this writing?
It doesn't work like that because WaitForExit() returns a bool, which doesn't have an ExitCode property. If you want this behavior on one line, you'll have to implement it yourself in a method and call it.
public int RunAndGetCode(string executable, string parameter)
{
var process = Process.Start(executable, parameter).WaitForExit();
return process.ExitCode;
}
// then later
var code = RunAndGetCode("Application.exe", "parameter");
So... That's not quite how Process works. You could write a wrapper class that allows you to do it in one one line, or using a using block, but when you have to wait for any process, that means you're locking up your own process while waiting for it. In Windows that is terrible practice.
The way it's designed in C#, it allows your own process to do other work while the process you called has returned. (Wrote this on mobile device; apologies for errors)
So, in short, no, but I see nothing wrong with this:
Process p = new Process();
P.Start();
While(!p.WaitForExit()) {
//do work while you wait for the calling process to return
}
var exitCode = p.ExitCode
I need to execute external program with arguments and get result from it (~1000 times with different arguments).
I found solution like this:
using System.Diagnostics;
...
Process process = new Process();
// Configure the process using the StartInfo properties.
process.StartInfo.FileName = "process.exe";
process.StartInfo.Arguments = "qwe 123";
process.Start();
process.WaitForExit();// Waits here for the process to exit.
// And check exit code for result
I need many repetitions with different arguments, but this code every time initiate new process. It is very expensive operation. I think, i can "load" ("save") process and repeat it without everytime initiating.
Or maybe exist other way to solve this problem?
If the started process is under your control it will be much more efficient to pass the input not via command line parameter for a new process instance every time, but re-program it so that the process reads its standard input line-wise in a loop, processes each line, and writes the result to its standard output. It's easy to hook up the calling program to the process' input and output:
// ...
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardInput = true;
// ...
process.Start();
StreamReader results = process.StandardOutput;
StreamWriter processInput = process.StandardInput;
foreach( var arg in args )
{
processInput.WriteLine(arg);
var oneResult = results.ReadLine();
// do something with this oneResult
}
This example assumes that each argument fits in one line (and each result fits in a line, too). Writing and reading a single line each time is our simple "protocol" for knowing when to start processing (on the process side) and when the result is complete (on the C# side).
I should perhaps add that a real program should add error handling and e.g. evaluate Process.Start()'s return value.
How can I run a console application in C#, passing parameters to it, and get the result of the application in Unicode? Console.WriteLine is used in the console application.
Important point is write Unicode in Console Application.
Sample from MSDN
// Start the child process.
Process p = new Process();
// Redirect the output stream of the child process.
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = "Write500Lines.exe";
p.Start();
// Do not wait for the child process to exit before
// reading to the end of its redirected stream.
// p.WaitForExit();
// Read the output stream first and then wait.
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
try with below code, here "Amay" is a argument.
System.Diagnostics.ProcessStartInfo info = new System.Diagnostics.ProcessStartInfo(#"E:\\ConsoleApplicationt\bin\Debug\ConsoleApplicationt.exe", "Amay");
System.Diagnostics.Process p = System.Diagnostics.Process.Start(info);
Check out Process.Start():
MSDN - Process.Start Method
Your code will probably look something like:
var process = Process.Start(pathToProgram, argsString);
process.WaitForExit();
var exitCode = process.ExitCode;
If by "result of the console application" you mean any output of the program to the console while it runs...you'll need to look at the documentation and figure out how to redirect the output of the program from the console to another stream.
Here http://www.aspcode.net/ProcessStart-and-redirect-standard-output.aspx You can see how to read the output from the console app You start with Process.Start().
Take a look at the Process class. You can call any executable using Process.Start("myexe.exe");
You should be careful depending upon your use some of the other examples can have issues. For common mistakes made writing your own code, read "How to use System.Diagnostics.Process correctly"
For a library to use, there is one here: http://csharptest.net/browse/src/Library/Processes
with a brief usage guide: "Using the ProcessRunner class"