How to read standard output of my own application - c#

I have an application that must read it's own output that is written via
Console.WriteLine("blah blah");
I'm trying
Process p = Process.GetCurrentProcess();
StreamReader input = p.StandardOutput;
input.ReadLine();
But it doesn't work because of "InvalidOperationException" at the second line. It says something like "StandardOutput wasn't redirected, or the process has not been started yet" (translated)
How can I read my own output ? Is there another way to do that ? And to be complete how to write my own input ?
The application with the output is running already.
I want to read it's output live in the same application. There is no 2nd app. Only one.

I'm just guessing as to what your intention might be but if you want to read the output from a application you started you can redirect the output.
// 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();
example from http://msdn.microsoft.com/en-us/library/system.diagnostics.process.standardoutput.aspx
Edit:
If you want to redirect the output of your current console application as your edit specifies you can use.
private static void Main(string[] args)
{
StringWriter writer = new StringWriter();
Console.SetOut(writer);
Console.WriteLine("hello world");
StringReader reader = new StringReader(writer.ToString());
string str = reader.ReadToEnd();
}

Related

Get Binary Output and Errors text of a process

I want to run an exe utility which produces some binary output (which is an image) based on given parameters.
I need to get the binary output of the utility as a stream (MemoryStream) and the Errors (if any).
It is also required to have a Timeout such that if app hangs or it takes a long time to respond, break its execution and return what ever result it output until that time.
One way is to use cmd like as:
cmd /C ... >out.jpg 2>err.txt
But this requires a temp file which is not acceptable, i want to capture output data directly as it should be called many times and output size is large.
The basic code to run the process is as following:
using (var p = new Process())
{
//p.StartInfo = new ProcessStartInfo("cmd", "/C " + command + " > x.jpg 2> err.txt");
p.StartInfo = new ProcessStartInfo()
{
FileName = #"x.exe", //dcraw
Arguments = #"-c -e x.jpg",
UseShellExecute = false, //it is usefull for non-exe like as for a Word file.
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden
};
int timeout = 5000; //msec
p.Start();
//MemoryStream outputStream = ...
//p.OutputDataReceived += ... //can't use this as it returns text not binary data
//string errorText = ...
p.WaitForExit();
//return new pResult(Image.FromStream(outputStream), errorText, isTimedout, ...);
}
How to get outputStream and errorText with specified timeout?
Note: I know there are several questions about process.start and output redirecting. Most of them are about capturing text output not binary data (like this or that). And this binary question does not answer my question completely as stated here (capturing both output and errors at the same time may cause dead-end locks, no timeout specified).

Read GnuPlot output into image in C#

I am using GnuPlot from a C# application. I'd like to read the GnuPlot PNG output directly from Standard Output rather than saving to a file and then reading it. My code looks like this right now:
string Path = #"C:\Program Files\gnuplot\bin\gnuplot.exe";
Process GnuplotProcess = new Process();
GnuplotProcess.StartInfo.FileName = Path;
GnuplotProcess.StartInfo.UseShellExecute = false;
GnuplotProcess.StartInfo.RedirectStandardInput = true;
GnuplotProcess.StartInfo.RedirectStandardOutput = true;
GnuplotProcess.Start();
StreamWriter SW = GnuplotProcess.StandardInput;
StreamReader SR = GnuplotProcess.StandardOutput;
SW.WriteLine("set terminal pngcairo size 300,200");
foreach (LoadCaseOutput LCO in LoadCases)
{
foreach (LoadCaseOutput.MemberOutput MO in LCO.Members)
{
SW.WriteLine("plot " + MO.GenerateAFEquation(P));
MO.AFImage = Image.FromStream(SR.BaseStream);
}
}
SW.WriteLine("exit");
GnuplotProcess.Close();
Right now this seems to stall at the Image.FromStream() line. What's going wrong?
update (I have updated my code to reflect my comment below)
It appears the problem is when the "exit" command is sent to gnuPlot. Without the exit command sent to gnuPlot, the program waits.
I took your example and was able to get the program to complete by moving the gnuPlot exit command up in the execution tree.
string Path = #"z:\tools\gnuplot\bin\gnuplot.exe";
Process GnuplotProcess = new Process();
GnuplotProcess.StartInfo.FileName = Path;
GnuplotProcess.StartInfo.UseShellExecute = false;
GnuplotProcess.StartInfo.RedirectStandardInput = true;
GnuplotProcess.StartInfo.RedirectStandardOutput = true;
GnuplotProcess.Start();
StreamWriter SW = GnuplotProcess.StandardInput;
StreamReader SR = GnuplotProcess.StandardOutput;
SW.WriteLine("set terminal pngcairo size 300,200");
SW.WriteLine("plot f(x) = sin(x*a), a = .2, f(x), a = .4, f(x)");
SW.WriteLine("exit");
Image png = Image.FromStream(SR.BaseStream);
png.Save(#"z:\tools\try3a.png");
GnuplotProcess.Close();
This did correctly generate a PNG file. For testing, I did try reading from the stream before sending the exit command. The program waits on the FromStream call.
Matt
The problem is not with the reading otherwise you would be getting an exception, replace the reading bit with this:
Image.FromStream(oFileStream, false, true)
It will validate the image as soon as it receives the first bytes and you go from there.

Why won't streamreader read the standard error stream?

So, I got a piece of advice on debugging my code, but my attempt to debug has sprouted up more errors. I have all the correct usings, but here's my issue:
Process p = new Process();
p.StartInfo.FileName = "javac";
Directory.CreateDirectory(System.IO.Path.Combine(Application.StartupPath + #"\TempJavaalfgwaepfgawe"));
p.StartInfo.Arguments = "-d " + System.IO.Path.Combine(Application.StartupPath + #"\TempJavaalfgwaepfgawe") + " " + files;
p.Start();
p.WaitForExit();
StreamReader sr = new StreamReader(p.StandardError);
MessageBox.Show(sr.ReadToEnd());
Anyhow, the error stems from when I declare the streamreader:
StreamReader sr = new StreamReader(p.StandardError);
I get the following two errors:
The best overloaded method match for 'System.IO.StreamReader.StreamReader(string)' has some invalid arguments
Argument 1: cannot convert from 'System.IO.StreamReader' to 'string'
The StandardError property of Process is already a StreamReader. There is no need to create a new one.
You do however need to redirect standard error before the process starts in order to read from it.
p.StartInfo.RedirectStandardError = true;
The p.StandardError property is already a StreamReader, so you can simply read it:
p.WaitForExit();
MessageBox.Show(p.StandardError.ReadToEnd());
Also don't forget to redirect the standard error before starting the process or youwill get an exception when you try to read it:
p.StartInfo.RedirectStandardError = true;
p.StandardError
is already a StreamReader, use that instance.

Reading System.Diagnostics.ProcessStartInfo StandardOutput as bytes instead of chars

I'm trying to automate svnadmin dump using C# ProcessStartInfo.
The way I've done it on the command line is like so,
svnadmin dump c:\Repositories\hackyhacky > c:\backup\hackyhacky.svn_dump
Works a treat and dumps successfully, and I can verify this by restoring it into another repository like so
svnadmin load c:\Repositories\restore_test < c:\backup\hackyhacky.svn_dump
Which restores successfully - yay!
Now... I need to replicate the command line piping into another file using C#, but for some reason
var startInfo = new ProcessStartInfo(Path.Combine(SvnPath, "svnadmin"),"dump c:\Repositories\hackyhacky")
{CreateNoWindow = true, RedirectStandardOutput = true,RedirectStandardError = true,UseShellExecute = false};
process.StartInfo = startInfo;
process.Start();
StreamReader reader = process.StandardOutput;
char[] standardOutputCharBuffer = new char[4096];
byte[] standardOutputByteBuffer;
int readChars = 0;
long totalReadBytes = 0;
// read from the StandardOutput, and write directly into another file
using (StreamWriter writer = new StreamWriter(#"C:\backup\hackyhacky.svn_dump", false)) {
while (!reader.EndOfStream) {
// read some chars from the standard out
readChars = reader.Read(standardOutputCharBuffer, 0, standardOutputCharBuffer.Length);
// convert the chars into bytes
standardOutputByteBuffer = reader.CurrentEncoding.GetBytes(standardOutputCharBuffer);
// write the bytes out into the file
writer.Write(standardOutputCharBuffer.Take(readChars).ToArray());
// increment the total read
totalReadBytes += standardOutputByteBuffer.Length;
}
}
Dumps the same repo into hackyhacky.svn_dump.
But when I run my load command line now
svnadmin load c:\Repositories\restore_test < c:\backup\hackyhacky.svn_dump
I get a checksum error weird-error!
svnadmin load c:\Repositories\restore_test < c:\backup\hackyhacky.svn_dump
< Started new transaction, based on original revision 1
* adding path : Dependencies ... done.
* adding path : Dependencies/BlogML.dll ...svnadmin: Checksum mismatch, fil
e '/Dependencies/BlogML.dll':
expected: d39863a4c14cf053d01f636002842bf9
actual: d19831be151d33650b3312a288aecadd
I'm guessing this is to do with how I'm redirecting and reading the StandardOutput.
Does anyone know the right way to mimic the command line file piping behaviour in C#?
Any help is greatly appreciated.
-CV
UPDATE
I've tried using a BinaryWriter and using the standardOutputByteBuffer to write to the file, but that doesn't work either. I get a different error about incorrect header format or something.
Alright! If you can't beat em, join em....
I found a post where the author pipes to a file directly within the Process StartInfo, and claims it works.
http://weblogs.asp.net/israelio/archive/2004/08/31/223447.aspx
It didn't work for me, as described in another gentleman's post
http://webcache.googleusercontent.com/search?q=cache:http://www.deadbeef.com/index.php/redirecting_the_output_of_a_program_to_a
He writes a batch file first with the piping and then executes it...
amWriter bat = File.CreateText("foo.bat");
bat.WriteLine("#echo off");
bat.WriteLine("foo.exe -arg >" + dumpDir + "\\foo_arg.txt");
bat.Close();
Process task = new Process();
task.StartInfo.UseShellExecute = false;
task.StartInfo.FileName = "foo.bat";
task.StartInfo.Arguments = "";
task.Start();
task.WaitForExit();
In his words:
Truly horrific, but it has the
advantage of working!
To be perfectly frank, I'm a bit annoyed this has taken me as long as it has, so the batch file solution works well so I'm going to stick with it.
The first thing I'd try is writing the character array - not the byte array - to the file.
That should work as long as the output is just simple text. There's other encoding issues, though, if the output is more complex: you're writing the file as UTF-8, whereas the default for command-line output (I believe) is Windows-1252.
I've been trying to do this very thing, and just stumbled on another solution to the problem used by Hector Sosa's svnmanagerlib sourceforge project:
The key to solving this was surrounding the call to WaitForExit() with
file operations. Also needed to make sure to write the output to disk.
Here are the relevant lines:
File.AppendAllText( destinationFile, myOutput.ReadToEnd() );
svnCommand.WaitForExit(); File.AppendAllText(destinationFile,
myOutput.ReadToEnd() );
Notice that I make a call to File.AppendAllText() twice. I have found
that the output stream does not write everything during the first call
to File.AppendAllText() on some occasions.
public static bool ExecuteWritesToDiskSvnCommand(string command, string arguments, string destinationFile, out string errors)
{
bool retval = false;
string errorLines = string.Empty;
Process svnCommand = null;
ProcessStartInfo psi = new ProcessStartInfo(command);
psi.RedirectStandardOutput = true;
psi.RedirectStandardError = true;
psi.WindowStyle = ProcessWindowStyle.Hidden;
psi.UseShellExecute = false;
psi.CreateNoWindow = true;
try
{
Process.Start(psi);
psi.Arguments = arguments;
svnCommand = Process.Start(psi);
StreamReader myOutput = svnCommand.StandardOutput;
StreamReader myErrors = svnCommand.StandardError;
File.AppendAllText(destinationFile, myOutput.ReadToEnd());
svnCommand.WaitForExit();
File.AppendAllText(destinationFile, myOutput.ReadToEnd());
if (svnCommand.HasExited)
{
errorLines = myErrors.ReadToEnd();
}
// Check for errors
if (errorLines.Trim().Length == 0)
{
retval = true;
}
}
catch (Exception ex)
{
string msg = ex.Message;
errorLines += Environment.NewLine + msg;
}
finally
{
if (svnCommand != null)
{
svnCommand.Close();
}
}
errors = errorLines;
return retval;
}

Executing BatchFile from C# program

I am writing the batch file and executing it through C# program.
Writing Batch file :
I will get the Path, Executable name and arguments from app.config and write them to a batch file.
Executing Batch file :
Once I write the batch file I pass the file name to below function which executes the batch file to launches an application.
Problem :
My program will write a lot of batch files which are executed immediately after each and every file is written. I find that, some times the applications are not started which means that batch files are not executed. I didn't even get any error messages or prompts for this failure of batch file execution.
Expected solution :
Any problem in executing the batch file, I should be able to log it or prompt an error.
Code that executes Batch File :
System.Diagnostics.ProcessStartInfo procinfo = new System.Diagnostics.ProcessStartInfo("cmd.exe");
procinfo.UseShellExecute = false;
procinfo.RedirectStandardError = true;
procinfo.RedirectStandardInput = true;
procinfo.RedirectStandardOutput = true;
System.Diagnostics.Process process = System.Diagnostics.Process.Start(procinfo);
System.IO.StreamReader stream = System.IO.File.OpenText(BatchPath + LatestFileName);
System.IO.StreamReader sroutput = process.StandardOutput;
System.IO.StreamWriter srinput = process.StandardInput;
while (stream.Peek() != -1)
{
srinput.WriteLine(stream.ReadLine());
}
Log.Flow_writeToLogFile("Executed .Bat file : " + LatestFileName);
stream.Close();
process.Close();
srinput.Close();
sroutput.Close();
I'm not sure where your problem lies specifically but I've had no problems with the following code:
using (FileStream file = new FileStream("xyz.cmd", FileMode.Create)) {
using (StreamWriter sw = new StreamWriter(file)) {
sw.Write("#echo ====================\n");
sw.Close();
}
}
Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.FileName = "xyz.cmd";
//p.StartInfo.RedirectStandardOutput = true;
p.Start();
//String s = p.StandardOutput.ReadLine();
//while (s != null) {
// MessageBox.Show(s);
// s = p.StandardOutput.ReadLine();
//}
p.WaitForExit();
Obviously that's been cut down a bit for the purposes of hiding my "secret sauce" but that's code currently being used in production without issues.
I do have one question. Why don't you execute the cmd file directly rather than running cmd.exe?
Probably the first thing I'd do is to print out the BatchPath + LatestFileName value to see if you're creating any weirdly named files which would prevent cmd.exe from running them.

Categories

Resources