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).
Related
I am running into an issue reading the output/input of a DOSBox Process started by my C# console application. DOSBox has an encoding for code page 437, but setting up my StreamReader with that encoding is giving me a NotSupportedException. It is worth the noting, the StreamReader/Writer are not throwing an error when being set up, but no input/output is being sent. Upon debugging, the exception is thrown throughout the object.
This is how I am starting my DOSBox Process:
ProcessStartInfo startInfo = new ProcessStartInfo()
{
FileName = "<path-to-DOSBox.exe>",
Arguments = "<path-to-game> -noconsole",
UseShellExecute = false,
RedirectStandardInput = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
};
process = new Process();
process.StartInfo = startInfo;
process.Start();
And then this is how I am attempting to read/write to the DOSBox console:
input = new StreamWriter(process.StandardInput.BaseStream, Encoding.GetEncoding(437));
input.WriteLine(command);
output = new StreamReader(process.StandardOutput.BaseStream, Encoding.GetEncoding(437));
Console.WriteLine(output.ReadToEnd());
I am registering the specific encoding with:
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
Here is a screenshot upon inspection of my StreamReader output:
I am trying to run a process in my code looping through each file and performing some task. Sometimes the EXE gets stuck when it runs across some files. And this doesn't move forward. If this kind of situation occurs how can I skip the files which is stuck in the process and save the file into a TXT file which has failed. And further move with the next file for processing. I tried to check it online and didn't get much help. Please suggest how can I get this done?
foreach (var file in files)
{
ProcessStartInfo processStartInfo = new ProcessStartInfo
{
FileName = exedir + "\\testing.exe",
Arguments = "-a -h -i -l " + file,
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true
};
using (Process exeProcess = Process.Start(processStartInfo ))
{
var line = string.Empty;
using (StreamReader streamReader = exeProcess.StandardOutput)
{
while (!streamReader.EndOfStream)
{
var content = streamReader.ReadLine();
// Doing some task here
}
}
exeProcess.WaitForExit(10000);
}
}
I am executing an ffmpeg command with a very complex filter and with a pipe within a C# application. I am putting images into the ffmpeg input stream (pipe)for rendering this images as overlays to the final video.
I want to render images with the pipe until the pipe closes. Unfortunately, I do not know how I can recognize that the pipe of the ffmpeg process has closed. Is there any possibility to recognize this event within C#?
The process is started like:
this._ffmpegProcess = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = this._ffmpegPath,
UseShellExecute = false,
RedirectStandardInput = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true
}
};
this._ffmpegProcess.StartInfo.Arguments = ffmpegCmd;
this._ffmpegProcess.OutputDataReceived += this.Proc_DataReceived;
this._ffmpegProcess.ErrorDataReceived += this.Proc_DataReceived;
this._ffmpegProcess.Exited += this._ffmpegProcess_Exited;
this._ffmpegProcess.Start();
this._ffmpegProcess.BeginOutputReadLine();
this._ffmpegProcess.BeginErrorReadLine();
The rendering happens within a timer:
this._renderOverlayTimer = new Timer(this.RenderOverlay);
this._renderOverlayTimer.Change(0, 30);
The timer is started right after starting the ffmpeg process:
private void RenderOverlay(object state)
{
using (var ms = new MemoryStream())
{
using (var img = GetImage(...))
{
img.Save(ms, ImageFormat.Png);
ms.WriteTo(this._ffmpegProcess.StandardInput.BaseStream);
}
}
}
The problem is that I always receive a "The pipe has ended" error at "ms.WriteTo()".
I now use a named pipe and trace the number of frames to process. The named pipe is closed right after the last frame is processed. This solution leads to a correct video without IOExceptions.
I have tried using (and have been advised against) using regular expressions for this task (here) - and so instead i tried using the HTMLAgilityPack in this manner however its resulting text is very poor, html lists (<ol><li></ol>) are completely lost and just result in clumped together paragraphs.
In this question i saw that lynx (compiled for windows) was recommended as a good alternative, however i am having trouble getting this working - how would one use lynx.exe to convert html (stored in a .net string) to a presentable plain text string with line breaks etc.
The only way i can think off is by writing the html to a file, using .nets system.process to call lynx.exe -dump and read the resulting file - this seems very clumsy.
Is there a better way of doing it?
What would the exact lynx.exe command line be for such a task?
The LYNX implementation i am using is this one:
http://invisible-island.net/datafiles/release/lynx-cs-setup.exe
Edit: Made some progress, this is the command line i've been using:
lynx.exe -dump "d:\test.html" >d:\output.txt
It sort of works but if i open the resulting file in notepad its all on one line (because lynx is only using Line Feed characters for new lines whereas notepad needs carriage returns to render properly.
Also, its inserting way too many line feeds after </li> & <br /> tags its doing two Line Feeds:
Hello, this is a normal line of text.
Next an ordered list:
1. The
2. Quick
3. Brown Fox
4. Jumped
I can work around this by replacing two consecutive LF's with just the one LF, but i'm still after a c# wrapper for all this.
Edit 2 - My final solution based on Christian's answer:
Function ConvertHtmlToPlainText(ByVal HtmlString As String) As String
'#### Define FileBuffer Path
Dim HtmlBuffer As String = WorkingRoot & "HtmlBuffer.html"
'#### Delete any old buffer files
Try
If File.Exists(HtmlBuffer) = True Then
File.Delete(HtmlBuffer)
End If
Catch ex As Exception
Return "Error: Deleting old buffer file: " & ex.Message
End Try
'#### Write the HTML to the buffer file
Try
File.WriteAllText(WorkingRoot & "HtmlBuffer.html", HtmlString)
Catch ex As Exception
Return "Error: Writing new buffer file: " & ex.Message
End Try
'#### Check the file was written OK
If File.Exists(HtmlBuffer) = False Then
Return "Error: HTML Buffer file was not written successfully."
End If
'#### Read the buffer file with Lynx and capture plain text output
Try
Dim p = New Process()
p.StartInfo = New ProcessStartInfo(LynxPath, "-dump -width 1000 " & HtmlBuffer)
p.StartInfo.WorkingDirectory = WorkingRoot
p.StartInfo.UseShellExecute = False
p.StartInfo.RedirectStandardOutput = True
p.StartInfo.RedirectStandardError = True
p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden
p.StartInfo.CreateNoWindow = True
p.Start()
p.WaitForExit()
'#### Grab the text rendered by Lynx
Dim text As String = p.StandardOutput.ReadToEnd()
Return text.Replace(vbLf & vbLf, vbLf)
Catch ex As Exception
Return "Error: Error running LYNX to parse the buffer: " & ex.Message
End Try
End Function
Using this you can invoke Lynx, grab the output from the redirected StandardOutput into a string without writing it to a file first.
using System;
using System.Diagnostics;
namespace Lynx.Dumper
{
public class Dampler
{
public void fdksfjh()
{
var url = "http://www.google.com";
var p = new Process();
p.StartInfo = new ProcessStartInfo("c:/tools/lynx_w32/lynx.exe", "-dump -nolist " + url)
{
WorkingDirectory = "c:/tools/lynx_w32/",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
WindowStyle = ProcessWindowStyle.Hidden,
CreateNoWindow = true
};
p.Start();
p.WaitForExit();
//grab the text rendered by Lynx
var text = p.StandardOutput.ReadToEnd();
Console.WriteLine(text);
}
}
}
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;
}