I am trying to write a piece of C# code which makes use of a small portion of Python code, and then try to output the Python code within my application. The goal is to have a Python RSS, which produces few lines of data and outputs it.
I hoped that a C# code would execute this code and capture all of the lines that are produced, but it captures only first one.
Here is a sample of what python output looks like:
Business Superjumbo jet future secured by Emirates order. A big order from Emirates airline saves the A380 after Airbus threatened to stop making the plane.
Business Banks create funds for firms hit by Carillion collapse. Lloyds and RBS will provide cash for struggling small firms as Nationwide takes on Carillion staff.
Politics Theresa May meets Macron with pledge of extra £44m for border. The UK pledges an extra £44m for channel border security ahead of the Anglo-French summit.
This is a short excerpt of the produced data, but C# ( result variable) only captures first line, sometimes two.
Am I doing something wrong or is not possible to get all prints using this method?
C# code
start.Arguments = "../test/smallWebCrawl.py";
start.UseShellExecute = false;// Do not use OS shell
start.CreateNoWindow = true; // We don't need new window
start.RedirectStandardOutput = true;// Any output, generated by application will be redirected back
start.RedirectStandardError = true; // Any error in standard output will be redirected back (for example exceptions)
try
{
using (Process process = Process.Start(start))
{
using (StreamReader reader = process.StandardOutput)
{
string stderr = process.StandardError.ReadToEnd(); // Here are the exceptions from our Python script
//Console.WriteLine(stderr);
string result = reader.ReadToEnd(); // Here is the result of StdOut(for example: print "test")
Console.WriteLine(result);
}
}
}
catch (Exception)
{
Console.WriteLine("Error. Wrong python path");
}
So I managed to find an alternative solution, which works and which I decided to use, although it is not the best, but I was keen on leaving the overall structure of the code, the way it was.
using (Process process = Process.Start(start))
{
using (StreamReader reader = process.StandardOutput)
{
string stderr = process.StandardError.ReadToEnd();
process.WaitForExit();
string result = reader.ReadToEnd();
string text = File.ReadAllText(start.WorkingDirectory + "\\t.txt");
So basically, I wait for the exit of my Python script to ensure that all data has been written to a file, then read the whole produced text file, which can be later deleted.
Related
I'm trying to solve a problem i got. My job is to make little app, that will show text which is inside of .txt file in the app window, but for some reason they told me that i have to use # ShellExecute(use Process.Start).
Is there even a way to do it? Because when i use ShellExecute, that file opens in notepad after button press, which is, I guess, point of using Shell.
There is little code of what i tried to do, but without success.
Thanks in advice!
string filePath = #"C:\Folder\file.txt";
ProcessStartInfo psi = new ProcessStartInfo(filePath);
psi.UseShellExecute = false;
psi.RedirectStandardOutput = true;
psi.CreateNoWindow = true;
var proc = Process.Start(psi);
string s = proc.StandardOutput.ReadToEnd();
textBox1.Text = s;
Instead of using ProcessStartInfo, try StreamReader like this :
string filePath = #"C:\Folder\file.txt";
StreamReader sr = new StreamReader(filePath);
string s = sr.ReadToEndAsync().GetAwaiter().GetResult();
Console.WriteLine(s);
Use Async method to read all text without blocking.
If you absolutely need to do that, you can create a second application TxtToTextBox, which you can run from your first application using Process.Start (initialize ProcessStartInfo with the path to that application instead of the txt file).
Then you can give that process an argument pointing to the file using psi.Arguments = $"\"{filePath}\"; (this also adds quotation marks around your path, so spaces are escaped).
Then in your second application you can do the sane thing, and simply read the file with File.ReadAllLines(args[0]) and print that into your text box.
If possible, I would recommend talking to whoever told you to use Process.Start and asking them for more reasons as to why you should use is, as this is one of the most roundabout ways to do this I could think of.
I have a 3rd party exe that accepts the path to a single text file via user input and creates new text files after doing some calculations. It is normally run via the console and accepts inputs one at a time.
I wrote a Winform application to allow us to mass generate these input files based on some parameters, and then pass those input files in to the exe one at a time.
It seems to be working fairly well so far, if I have it generate 2 or 3 input files, the output files shortly appear in the correct folder. But if I generate and pass through 10 files (one at a time), the first 3 or so files appear correctly, but then the later ones do not.
I thought that it was that perhaps my program was outpacing the exe's calculations, so I put some pauses in between the console inputs. That didn't change anything, but I found out that once I close out of my winform, all of the late files appear in my folder all at once.
Any thoughts on what is happening here?
This is my first time using Process, so it's probably not ideal. I was working originally with not putting in the next input until the exe was at the correct line, but I was struggling with reading the output. Then I deleted what I had and wrote the simple bit below, not expecting that to be enough. Apparently it was and I actualy started getting results.
Thanks!
Process p = new Process();
ProcessStartInfo start = new ProcessStartInfo();
start.CreateNoWindow = true;
start.UseShellExecute = false;
start.FileName = #"pathtoexe";
start.WindowStyle = ProcessWindowStyle.Hidden;
start.WorkingDirectory = Path.GetDirectoryName(start.FileName);
start.RedirectStandardOutput = true;
start.RedirectStandardInput = true;
start.RedirectStandardError = true;
p.Start();
StreamWriter writer = p.StandardInput;
writer.WriteLine("9"); // exe asks where the dll is stored, 9 indicates the path we use
Thread.Sleep(1000);
writer.WriteLine("1"); // exe is asking which type of output files we want, 1 indicates the basic text output calculations
Thread.Sleep(1000);
for (int j = 1; j<= _mun.Count;j++)
{
writer.WriteLine(#"..\Inputs\GeneratedInputs_"+j+".in"); //The input that is read by one run of the exe. We create these files before getting to this step
Thread.Sleep(1000);
writer.WriteLine(""); // exe asks us to confirm by entering a character. Just hitting enter works
Thread.Sleep(1000);
writer.WriteLine("2"); //the exe asks us which calculations we are running. 2 is the result we want
Thread.Sleep(5000);
if (j == _mun.Count) //The last input of the exe is if we want to run another run again or exit. On the last item of our list, choose "exit" with 0 instead of run again, with 1.
{
writer.WriteLine("0");
// p.WaitForExit(); I'm not really sure where in all of this I should be putting a WaitForExit, if at all.
}
else
{
writer.WriteLine("1");
}
Thread.Sleep(1000);
}
You can call
writer.Flush()
To clear the buffer and ensure all data is written.
You should also consider wrapping your writer instance in a using clause to ensure proper disposal
using (StreamWriter writer = writer = p.StandardInput)
{
writer.WriteLine("test");
}
I'm executing a batch file from my C# application using the System.Diagnostics classes, updating my GUI with the output along the way, but as soon as my batch file is in excess of a certain number of lines, the process just hangs. The exact amount of lines seems to vary, but I have been able to reproduce it with a simple batch file that prints "Hello Kitty" 316 times:
#echo off
echo Hello Kitty
echo Hello Kitty
etc.
If I remove the 316th line, the batch file executes fine and the forms application behaves as expected, but any more lines causes the process to suspend indefinitely, not producing even one of the first 300 hello kitties.
Here is my code for executing the batch file:
process = new System.Diagnostics.Process();
process.StartInfo.FileName = batchName;
process.StartInfo.Arguments = " < nul";
process.StartInfo.CreateNoWindow = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.UseShellExecute = false;
process.Start();
with these declared elsewhere:
protected Process process;
protected StreamReader output;
My main form does something like this (simplified a little):
string output;
while (!proc.Process.HasExited)
{
proc.Process.WaitForExit(200);
if (proc.Process.HasExited)
{
output = proc.Output.ReadToEnd();
rtbStatus.AppendText(output);
}
Application.DoEvents();
}
I don't understand why it does this, and no examples that I find on the net make any mention of a size limit on batch files. Please advise.
You need to read the output continuously - you cannot wait to the end.
Output will be buffered, so a small amount (usually about 4KB) can be written before the process hangs.
The alternative is to direct the output to a file then read the file.
I have a program that reads text files filled with code designed to be executed line by line by the program, like a batch file. The problem is that I don't no how to do the line executing part. Here is my code, I thought using the \r would fool the console. But it just shows me a list of lines in the file.
if (tok[0] == "read" && length == 2)
{
try
{
StreamReader tr = new StreamReader(#"C:\Users\Public\"+tok[1]+".txt");
while (!tr.EndOfStream)
{
Console.WriteLine(tr.ReadLine());
}
}
catch
{
Console.WriteLine("No such text file.\n");
}
Prompt();
}
If I knew what to search for to fix my problem in Google, I would have. But I've got no idea.
Thanks
EDIT - My program is a crude synthesizer. It takes inputs in the form of 440 5, or 415 2. The first number is frequency, the second duration. What I'm wanting to do is read text files, which my code does, and execute the sound info line by line, which it doesn't, hence my question. It works perfectly fine from standard input.
Audio synthesis is not straightforward, there used to be
Console.Beep(frequency,duration);
but that's using the PC speaker most systems don't have anymore - here's an example though using DirectSound to achieve something close to what you want.
To read the frequency and duration from your text file you can use something like this (splitting on space):
StreamReader tr = new StreamReader(#"test.txt");
while(!tr.EndOfStream)
{
string[] parts = tr.ReadLine().Split(new[]{' '});
int frequency = Convert.ToInt32(parts[0]);
int duration = Convert.ToInt32(parts[1]);
}
You should load your code and compile it in the runtime.
Check out following examples:
http://www.csharpfriends.com/articles/getarticle.aspx?articleid=118
http://www.codeproject.com/KB/dotnet/evaluator.aspx
EDIT:
You should use Process.Start(cmd); to execute commands in the shell. Here I've found few nice examples: http://dotnetperls.com/process-start
If your program works fine using Standard Input just pass the text file to it like this:
yourprogram.exe < textFile.txt
Then the contents of the text file will be passed to your program on Standard Input.
I running an exe from a .NET app and trying to redirect standard out to a streamreader. The problem is that when I do
myprocess.exe >> out.txt
out.txt is close to 14mb.
When I do the command line version it is very fast but when I run the process from my csharp app it is excruciatingly slow because I believe the default streamreader flushes every 4096 bytes.
Is there a way to change the default stream reader for the Process object?
I haven't tried, but it looks like the asynchronous methods may offer better performance. Instead of using process.StandardOutput, try this method instead:
Process process = Process
.Start(new ProcessStartInfo("a.exe"){RedirectStandardOutput = true});
if (process != null)
{
process.OutputDataReceived += ((sender, e) =>
{
string consoleLine = e.Data;
//handle data
});
process.BeginOutputReadLine();
}
Edit: Just realized I'm answering the wrong question. In my case the stdout buffer was full and WaitForExit() was blocking forever, because nothing was reading from the buffer yet. So if you have THAT problem, then here's a solution. ;)
This is my first day with C# so please understand that this might not be the best solution, and might not always work. But it works in the 2x I've tested it. ;) This is synchronous, just start start writing the redirected stdout/stderr to the file before you WaitForExit(). This way WaitForExit() won't block waiting for the stdout buffer to be emptied.
string str_MyProg = "my.exe";
string str_CommandArgs = " arg1 arg2"'
System.Diagnostics.ProcessStartInfo procStartInfo = new System.Diagnostics.ProcessStartInfo(str_MyProg, str_CommandArgs);
procStartInfo.RedirectStandardError = true;
procStartInfo.RedirectStandardOutput = true; // Set true to redirect the process stdout to the Process.StandardOutput StreamReader
procStartInfo.UseShellExecute = false;
procStartInfo.CreateNoWindow = true; // Do not create the black window
// Create a process, assign its ProcessStartInfo and start it
System.Diagnostics.Process myProcess = new System.Diagnostics.Process();
myProcess.StartInfo = procStartInfo;
myProcess.Start();
// Dump the output to the log file
string stdOut = myProcess.StandardOutput.ReadToEnd();
StreamWriter logFile = new StreamWriter("output.txt" );
logFile.Write(stdOut);
logFile.Close();
myProcess.WaitForExit();
Yes, that's about right. There is a buffer that stores the process output, usually between 1 and 4KB in the common CRT implementations. One small detail: that buffer is located in the process you start, not the .NET program.
Nothing very special needs to happen when you redirect to a file, the CRT directly writes it. But if you redirect to your .NET program then output goes from the buffer into a pipe. Which then takes a thread switch to your program so you can empty the pipe. Back and forth a good 700 times.
Yes, not fast. Easily fixed though, call setvbuf() in the program you are running to increase the stdout and stderr output buffer sizes. Then again, that takes having the source code of that program.
Anticipating a problem with that: maybe you ought to use cmd.exe /c to get the redirection to a file, then read the file.
The Process class exposes the stdout stream directly, so you should be able to read it at whatever pace you like. It's probably best to read it in small chunks and avoid calling ReadToEnd.
For example:
using(StreamReader sr = new StreamReader(myProcess.StandardOutput))
{
string line;
while((line = sr.ReadLine()) != null)
{
// do something with line
}
}
This worked out for me:
var sb = new StringBuilder();
while (!proc.StandardOutput.EndOfStream)
{
sb.Append(proc.StandardOutput.ReadToEnd());
proc.StandardOutput.DiscardBufferedData();
}