I have a (C#) console application which maintains a state. The state can be altered by feeding the application with various input through the console. I need to be able to both feed the application with a bit of input, then read the output rinse and repeat.
I create a new process and do all of the normal work of redirecting the input/output. The problem is that after I've sent input and call ReadLine() on the standard output it does not return a value before I call Close() on the standard input after which I cannot write anymore to the input stream.
How can I keep open the input stream while still receiving output?
var process = new Process
{
StartInfo =
{
FileName =
#"blabal.exe",
RedirectStandardInput = true,
RedirectStandardError = true,
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true,
ErrorDialog = false
}
};
process.EnableRaisingEvents = false;
process.Start();
var standardInput = process.StandardInput;
standardInput.AutoFlush = true;
var standardOutput = process.StandardOutput;
var standardError = process.StandardError;
standardInput.Write("ready");
standardInput.Close(); // <-- output doesn't arrive before after this line
var outputData = standardOutput.ReadLine();
process.Close();
process.Dispose();
The console application I'm redirecting IO from is very simple. It reads from the console using Console.Read() and writes to it using Console.Write(). I know for certain that this data is readable, since I have another application that reads from it using standard output / input (not written in .NET).
That is happening because of you are using Write("ready") which is will append a string to the text, instead use WriteLine("ready"). that simple :).
Related
I am redirecting Process.StandardOutput and Process.StandardError from a System.Diagnostics.Process that uses 7zip to extract and zip archives and am unable to read the progress from the process.
It appears, 7Zip like some other applications, emit are backspace and delete characters to partially write a line data and then and delete the written characters using backspace and delete in order o show progress. I am trying to read those partial line outputs from the target process am unable to do so. However, I may be wrong in this assumption.
What I have tried:
var process = new Process
{
StartInfo =
{
FileName = command,
Arguments = arguments,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true
}
};
process.Start();
After the above code block, I have tried using various methods of reading the data.
I have tried the async event handlers:
process.OutputDataReceived += (sender, args) => { Console.WriteLine(args.Data); };
process.ErrorDataReceived += (sender, args) => { Console.WriteLine(args.Data); };
process.BeginOutputReadLine();
process.BeginErrorReadLine();
I have tried using the async methods of the StandardOutput:
while (!process.StandardOutput.EndOfStream)
{
char[] buffer = new char[256];
int read = process.StandardOutput.ReadAsync(buffer, 0, buffer.Length).Result;
Console.Write(buffer, 0, read);
}
and
process.StandardOutput.BaseStream.CopyToAsync(Console.OpenStandardOutput());
And have tried using the async methods of the underlying base stream.
while (!process.StandardOutput.EndOfStream)
{
byte[] buffer = new byte[256];
int read = process.StandardOutput.BaseStream.ReadAsync(buffer, 0, buffer.Length).Result;
string data = Encoding.UTF8.GetString(buffer, 0, read);
Console.Write(data);
}
As an example, run 7Zip from the terminal with the following command:
"c:\program files\7-zip\7z.exe" x -o"C:\target" "K:\Disk_23339.secure.7z"
This shows progress output when running directly in a command prompt, with each success progress incrementing overwriting the previous:
It then uses backspace chars to overwrite the previous progress.
Running the same command and arguments using Process.Start()
var process = new Process
{
StartInfo =
{
FileName = "c:\program files\7-zip\7z.exe",
Arguments = 'x -o"C:\target" \"K:\Disk_23339.secure.7z\"",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true
}
};
process.Start();
process.StandardOutput.BaseStream.CopyToAsync(Console.OpenStandardOutput());
process.WaitForExit();
When running this and attempting to read the redirected standard output of the process characters that are not emitted from the source process that do not contain a new line (either by linefeed or carriage return + line feed) are output to the standard output or standard error of System.Diagnostics.Process and hence never written to the console.
7zip of course is just one example. This issue also occurs with numerous PowerShell and Python scripts.
Is there anyway to read these characters from Process.StandardOutput and Process.StandardError.
I am not sure but I think the issue is the underlying stream reader reads one line at a time and never returns these partial lines because they never include line ending characters.
Though the post had been months, in case your problem (not able to get progress from 7zip when execute in command line) still exists, use the "-bsp1" switch in command line argument.
I was also looking for the solution of same issue and just got it (tested successfully in my case) right. By redirect StandardOutput, repeatedly do cmd.StandardOutput.ReadLine() (the Synchronous method, I tested this method instead of asynchronous method that use EventHandler, but I think async method would work too), and use RegExp to detect the progress to update my UI.
My command (run in .NET)
C:\Program Files\7-Zip\7z.exe a -t7z -mx=9 -bsp1 "G:\Temp\My7zTest_Compressed.7z" "G:\SourceFolder"
Credit to #justintjacob
How to show extraction progress of 7zip inside cmd?
Console.InputEncoding = Encoding.Unicode;
Console.OutputEncoding = Encoding.GetEncoding("utf-8");
Console.WriteLine("开始测试starttest전소미123");
string input = Console.ReadLine();
Console.WriteLine(input);
Console.ReadKey();
Console.ReadKey();
when I run these codes, I get exactly the same string, as below
And I can't get the right characters if I don't set InputEncoding or OutputEncoding or both.
But I can't set Console.OutputEncoding in a winform program. It threw An unhandled exception of type 'System.IO.IOException' occurred in mscorlib.dll. This error happened on the second line below,
Console.OutputEncoding = Encoding.Unicode;
code
Console.InputEncoding = Encoding.Unicode;
Console.OutputEncoding = Encoding.Unicode;
Process process = new Process()
{
StartInfo = new ProcessStartInfo
{
FileName = $".\\test.exe",
Arguments = "",
CreateNoWindow = true,
UseShellExecute = false,
ErrorDialog = true,
//AutoFlush = true,
//RedirectStandardInput = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
StandardOutputEncoding = Encoding.GetEncoding("utf-8"),
},
};
process.Start();
string text = process.StandardOutput.ReadLine();
I had searched similar questions online and tested many times for hours, just can't figure it out.
What text editor and encoding are you using for the exe file? I think for example Notepad's default encoder is ANSI which I know do not support Asian symbols. If you want to change the encoder for Notepad click File -> Save as, then you can change the encoding just left to the Save button and change the encoding to UTF-8 or Unicode.
I have two applications:
WPF that is a client
Console Application that is WCF service hosted stand-alone
I want to be able to start process through WCF and return an output stream from that process (so I will use process.StandardOutput.BaseStream).
The problem is that once process starts, the returning object which is output stream is not being returned until process exits.
TransferMode in my both programatically created bindings is set to Streamed.
Uploading files TO WCF works fine, but problem exists only with returning stream.
WCF Service Code:
[OperationContract]
Stream RunTests(string testPackageDll);
Implementation:
public Stream RunTests(string testPackageDll)
{
var p = new Process()
{
StartInfo =
{
FileName = #"C:\Tests\NUnit-2.6.4\nunit-console.exe",
Arguments = $#"C:\Tests\{testPackageDll} /xml=test-results.xml",
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
}
};
try
{
p.Start();
p.BeginOutputReadLine();
return p.StandardOutput.BaseStream;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return null;
}
}
WPF Application Code:
public void RunTests(string testToRun)
{
string streamLine;
using (TextReader reader = new StreamReader(_serviceHandler.Service.RunTests(testToRun)))
{
streamLine = reader.ReadLine();
while (streamLine != null)
{
WriteToConsole(streamLine);
streamLine = reader.ReadLine();
}
}
}
the _serviceHandler.Service.RunTests(testToRun) method does not return Stream as it suppose to do, instead it is waiting for process to end then returns stream.
What I want to achieve is to read "live" stream straight from process.
UPDATE
I just found out that Stream is actually being streamed through WCF but only when the output text buffer reaches 12-16 kb, so what I am looking for now is how to change frequency (probably buffer size if there's a chance) of the data.
I know it would be possible by "chunking" the stream into small parts, but maybe there's some better solution than that?
My C# program needs to send data to a 3rd-party program via its standard input. However, the program waits for the input stream to reach EOF before processing. Here my code:
// Starts the process.
var process = new Process();
process.StartInfo.CreateNoWindow = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.FileName = "foo.exe";
process.Start();
// Sends data to child process.
var input = process.StandardInput;
input.WriteLine("eval('2 * PI')");
input.Flush();
input.Close();
// Reads the result.
var output = process.StandardOutput;
var result = output.ReadLine();
The child program won't do anything and my C# code becomes stuck at output.ReadLine() call. However if I kill the C# process, then the child starts to work exactly on the data I've sent. How can I make the child encounter an EOF while I'm still alive?
StreamWriter might not be sending an actual eof when it closes the stream. You could try writing your own to the stream just before you close it. Something like this might work:
input.Write((char)26);
You may have to find out what the process expects for eof.
I want to be able to read a screen shot of a web site, and am attempting to use phantomjs and ASP.NET.
I have tried using page.render which would save the screen shot to a file. It works as a console application, but not when I call it from an asp.net handler. It is probably due to file permissions, since simple applications (like hello.js) work fine.
That is okay, my preference would be not to write to a file, but to deal with the bytes and return an image directly from the handler.
I am a bit lost as to how to do that. I noticed a method called page.renderBase64, but do not know how to use it.
Currently I am using an IHttpHandler.
There is a similar question here, but that person eventualy dropped phantomjs. I like the look of it and want to continue using it if possible.
Running Phantomjs using C# to grab snapshot of webpage
According to your last comment you can do the following in phantom js file:
var base64image = page.renderBase64('PNG');
system.stdout.write(base64image);
in C#:
var startInfo = new ProcessStartInfo {
//some other parameters here
...
FileName = pathToExe,
Arguments = String.Format("{0}",someParameters),
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
RedirectStandardInput = true,
WorkingDirectory = pdfToolPath
};
var p = new Process();
p.StartInfo = startInfo;
p.Start();
p.WaitForExit(timeToExit);
//Read the Error:
string error = p.StandardError.ReadToEnd();
//Read the Output:
string output = p.StandardOutput.ReadToEnd();
In your output variable you can read base64 returned from phantomJS and then do what you have planned with it.
Use the wrapper for Phantomjs from here nreco wrapper
You can get js for rastor here : rastorize
And then the following code in C# would do the job.
var phantomJS=new PhantomJS();
phantomJS.Run("rasterize.js", new[] { "http://google.com","ss.pdf" });
This question stemmed from my lack of understanding of what a base64 string actually was.
In the javascript file that phantomjs runs, I can write the base64 image directly to the console like so:
var base64image = page.renderBase64('PNG');
console.log(base64image);
In the c# code that runs phantomjs, I can convert the console output back to bytes and write the image to the response, like so:
var info = new ProcessStartInfo(path, string.Join(" ", args));
info.RedirectStandardInput = true;
info.RedirectStandardOutput = true;
info.UseShellExecute = false;
info.CreateNoWindow = true;
var p = Process.Start(info);
p.Start();
var base64image = p.StandardOutput.ReadToEnd();
var bytes = Convert.FromBase64CharArray(base64image.ToCharArray(), 0, base64image.Length);
p.WaitForExit();
context.Response.OutputStream.Write(bytes, 0, bytes.Length);
context.Response.ContentType = "image/PNG";
This seems to avoid file locking issues I was having.
Using CasperJS coupled with PhantomJS , I've been getting beautiful shots of webpages.
var casper = require('casper').create();
casper.start('http://target.aspx', function() {
this.capture('snapshot.png');
});
casper.run(function() {
this.echo('finished');
});
I highly recommend you check out that tool. I'm still not sure how to do the post-backs though..
Set the 'WorkingDirectory' property of ProcessStartInfo object in order to specify the saving location of the file.