load and execute external program (many times) - c#

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.

Related

How to pass values from a process being called

In the main method I want to start a process which is to run some test cases, and when the process returns I want to know how many test cases failed. In this case, I want the process to be able to return a value to the main process after it is done executing. Is there a way to do this? My code is like below:
ProcessStartInfo processInfo = new ProcessStartInfo(exeFilePath, Parser.Default.FormatCommandLine(options));
var p = Process.Start(processInfo);
// need to get the number of failed test cases from the process
Any help is appreciated.

Listen for cmd output and log to file

I'm trying to make a C# program that can listen for and output to cmd.exe and log it to file. For example, if I run an exe and it runs a command in cmd like echo "hello", I want echo "hello" to be written in a file.
I know I need to use FileSystem, as well as Process maybe?
If this is even possible, and help would really be appreciated. Thanks.
Here's a quick little example that should work. There are a lot of examples out there, I'll try looking on stackoverflow and post one as well...
string cmd_to_run = "dir"; // whatever you'd like this to be...
// set up our initial parameters for out process
ProcessStartInfo p_info = new ProcessStartInfo();
p_info.FileName = "cmd";
p_info.Arguments = "/c " + cmd_to_run;
p_info.UseShellExecute = false;
// instantiate a new process
Process p_to_run = new Process();
p_to_run.StartInfo = p_info;
// wait for it to exit (I chose 120 seconds)
// waiting for output here is not asynchronous, depending on the task you may want it to be
p_to_run.Start();
p_to_run.WaitForExit(120 * 1000);
string output = p_to_run.StandardOutput.ReadToEnd(); // here is our output
Here's the Process class MSDN overview (there's a quick example on this page): https://msdn.microsoft.com/en-us/library/system.diagnostics.process(v=vs.110).aspx
And here's a SO example dealing with calling ReadToEnd() on Process: StandardOutput.ReadToEnd() hangs

Reading StdOut after process has been abnormally terminated using Process.kill

i am invoking an exe via c# Diagnostics.Process class and read output from it's StdOut. The process is forcefully terminated in case it doesn't automatically terminates in a specified time, something like:
process.StartInfo.FileName = #"D:\t.exe";
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.RedirectStandardInput = false;
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = false;
process.WaitForExit(500);
if (!process.HasExited)
{
process.Kill();
process.WaitForExit();
}
string stdOutContents = process.StandardOutput.ReadToEnd();
Now the problem is the code works succesfully when the exe terminates normally. But in case it fails to terminate normally (usually the exe gets stuck in an infinite loop somewhere), stdOutContents is read as an empty string.
How can i read StdOut after the process is killed (without using process.OutputDataReceived event technique)? (It has been verified that the exe-in-question does always writes something onto StdOut even if it gets stuck somewhere).
Update 1
Details about Exe which is being invoked (refereed as 'native app' across this question)
It is a small utility implemented in c language and compiled using MS C++ compiler. It does its job while simultaneously outputting status information onto the StdOut (using putchar).
There are only two possible cases of operation:
It will run successfully while simultaneously printing some data onto the StdOut.
It will run normally to a certain point (simultaneously outputting data on StdOut) and then get stuck in an infinite loop. (This is an acceptable behavior).
Both scenarios have been verified using cmd.
Details about new attempts
i wrote a c# app (referred as dummy app) which mimics the native app behavior and this code works fine. However when run for the native app, i get nothing at all.
i don't understand why the code cant read the contents outputted by the native app?
i also tried using event handler for OutputDataReceived. It gets called only once with args.Data = null when the code tries to kill the process. Inspecting the behavior for dummy app revealed that when process.kill is called, the handler is invoked with args.Data = null. So this seems to be a standard behavior of sorts for both apps.
i also tried changing the newline characters for native app. Since it is implemented in c language, it uses \n for newline. i tried using both \r\n pair for newline but StdOut is still blank (for case 2).
I had the same interrogation and the doc of Process.Kill says
Data edited by the process or resources allocated to the process can be lost if you call Kill.
Which seems to indicate that you cannot rely on reading the StandardOutput of a process, although it is not clearly stated that the output / error streams are disposed.
I finally got inspired by this answer
How to spawn a process and capture its STDOUT in .NET?
and I use the following code :
var info = new ProcessStartInfo("some.exe");
info.CreateNoWindow = true;
info.RedirectStandardOutput = true;
info.UseShellExecute = false;
using (var p = new Process())
{
p.StartInfo = info;
var output = new StringBuilder();
p.OutputDataReceived += (sender, eventArgs) =>
{
output.AppendLine(eventArgs.Data);
};
p.Start();
p.BeginOutputReadLine();
if (!p.WaitForExit(5000))
{
Console.WriteLine("Taking too long...");
p.Kill();
Console.WriteLine("Process killed, output :\n" + output);
}
}
Same pattern can be used with the ErrorDataReceived
Note that one could miss some unflushed output from the child process, however in my case I don't expect much from a process that requires to be killed, at most some information for debugging purposes.

ReadToEnd from std output of process and waitforexit

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

Why can't I invoke a Perl script with ASP.NET's System.Diagnostics.Process?

I'm trying to use the System.Diagnostics.Process class to run a Perl script from within an ASP.NET application. The Perl command runs fine when I type it into the command line, but when ASP.NET tries to run the same command it doesn't work.
I suspect it may have to do with input and output stream. The Perl script takes a long time to run to completion on the command line. But in .NET the command returns almost immediately, and when I try to redirect Perl script's standard output to see what messages are yielded it shows just the first line and returns. Using blocking stream reading techniques doesn't fix this; .NET really thinks that the Perl script is outputting just one line, then finishes.
Any suggestions? As much as I would like to, the script can't be rewritten in .NET. Here's the code I'm using to launch the process. I've tried changing ReadToEnd() to a series of ReadLine() calls, but after the first call returns the first line of output the next call returns null.
Process proc = new Process();
proc.StartInfo.FileName = scriptPath;
proc.StartInfo.Arguments = arguments;
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.UseShellExecute = false;
proc.Start();
string output = proc.StandardOutput.ReadToEnd(); // Returns only the first line
proc.WaitForExit();
Switch the 2 last lines:
lineproc.WaitForExit();
string output = proc.StandardOutput.ReadToEnd(); // Returns only the first
or build a loop that keeps reading from proc.StandardOutput until the script has finished.

Categories

Resources