How to keep standard input stream open? - c#

I am redirecting all streams (in/out/error) from a commandline application that runs some ChaiScript scripts. The user is able to interact with a running script via input commands such as PromptForDouble("Enter a double:"); which are defined in the separate commandline application and not my C# program.
Right now I have a RichTextBox that behaves like a console, and is readonly. I have another textbox at the bottom that can be used to "inject" input when needed. However, after the script is tossed into standard input, I'm forced to close standard input, and obviously can't "reopen" it once the script asks the user for input.
My question is how can I "close" standard input after the script is entered, continue with the application (receive output), and enter input again when the user is prompted?
Relevant code:
// Executes the script in the specified workspace (workspace only specified for logging)
public ScriptOutput ExecuteScript(WorkArea workspace, string script)
{
ScriptOutput output;
output.StdOut = "";
output.StdErr = "";
process.Start(); // the instance of the commandline program
StreamWriter inStream = process.StandardInput;
inStream.WriteLine(script + "\n//<SCRIPT END>\n");
inStream.Close();
output.StdErr = process.StandardError.ReadToEnd();
string line = "";
while((line = process.StandardOutput.ReadLine()) != null)
{
workspace.log(line, false);
}
process.WaitForExit();
//inStream.Close(); <-- closing the stream here makes the program "pause" after the script is entered as it expects more input
return output;
}
ScriptOutput is just a lazy way of having stdout and stderr strings in one object (it's a struct). This was coded before tuples so...Sorry :P
tl;dr how do I emulate closing and reopening standard input?

Related

c# start a exe until it is completely started and then append the arguments

I was trying to start a exe with arguments by Process.Start.
My first try is using Process.Start("Path/of/the/exe", "arguments of exe").
Here's my code snippets:
Process.Start(#"D:\Program Files\ITASCA\UDEC700\Exe64\udecConsole2017.exe", #"call 'D:\Work\202205\20220525\tunnel-for-cmd.txt'");
However the initialization of this exe is a bit slow, and the result is, I can only start the exe but the failed passing arguments. The following is the screenshot:
which is exactly the same result that starts without arguments.
By referencing this post C# - Making a Process.Start wait until the process has start-up, I changed my code as follows:
var process = Process.Start(#"D:\Program Files\ITASCA\UDEC700\Exe64\udecConsole2017.exe", #"call 'D:\Work\202205\20220525\tunnel-for-cmd.txt'");
while (string.IsNullOrEmpty(process.MainWindowTitle))
{
System.Threading.Thread.Sleep(100);
process.Refresh();
}
however these changes does not work.
I think my goal is to wait until exe completely started and then run it with arguments, but I dont know how to implement this.
=====================================================
New additions:
if I type in arguments call 'D:\Work\202205\20220525\tunnel-for-cmd.txt' in this started process, I will get my result:
SO I think the input arguments should be OK?
=======================================
new addition 2:
code for checking outputstream end
It appears this is a console application and you are typing in the console after it starts. This typing is not arguments: Arguments are provided only when starting a new process and never change.
What you are doing is providing something to the standard input of the program. Console programs have three streams the OS provides (one input and two output). You need to redirect these to detect when the program has started and to provide the proper input.
Something like this:
// Start with stdio redirected
var psi = new ProcessStartInfo()
{
UseShellExecute = false,
FileName = #"your exe",
RedirectStandardInput = true,
RedirectStandardOutput = true,
};
var p = System.Diagnostics.Process.Start(psi);
// Read until the udec> prompt
while(true)
{
var line = p.StandardOutput.ReadLine();
if(line.StartsWith("udec>"))
break;
}
// Write the command
p.StandardInput.WriteLine(#"call 'D:\Work\202205\20220525\tunnel-for-cmd.txt'");
// Read the result
p.StandardOutput.ReadToEnd();

Capture standard output of a python program as it happens

I'm trying to write a C# program that captures the standard output in a python program. My problem is that all of the output comes after the program has executed rather than when it actually happens. As an example, for this python program:
print "Hello"
time.sleep(2)
print "Hello"
I would expect to get "Hello", a two second gap, and then another "Hello". The actual result is a two second gap and then "Hello", "Hello".
If I run the above python script from the command line, I get the desired behaviour. If the command prompt can do this, then I should be able to mimic that functionality without having to flush the buffer repeatedly.
I'm using this to run the process from C#:
_proc = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "C:\\Python27\\python.exe",
Arguments = pyScript,
RedirectStandardError = true,
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
}
};
_proc.OutputDataReceived += ProcOnOutputDataReceived;
_proc.Start();
_proc.BeginOutputReadLine();
I can run this C# code (and changing the ProcessStartInfo properties above to run C# executable) and it behaves correctly:
Console.WriteLine("Hello");
Thread.Sleep(2000);
Console.WriteLine("Hello");
With this code I get "Hello", a two second gap, and then another "Hello".
Any idea why? How can I get the python interpreter to send the standard output as it happens?
I know this is old but running python with -u option (unbuffered) seems to be what you were after.
Unbuffered will not wait before flushing output.
Running python without this option, output is flushed when:
The buffer is full
You call sys.stdout.flush()
The process ends
You need to flush the output buffer.
import sys
sys.stdout.flush()
See this question: How to flush output of Python print?
Good
(Capture standard output of a python program as it happens)
Em console is NEED:
import sys
sys.stdout.flush()

Output from py2exe converted program gets received all at once

I have made a python console script, and converted it to a .exe with py2exe (I have used the console = ['test.py'] line in my setup file).
The program parses a file, and during the parse it prints out how much of the file it has parsed. Typical output would be:
Processing (currently at 1%)
Processing (currently at 4%)
etc.
When running the file in a cmd window it works just as expected.
I have also created a very small C# WPF program that just runs the parser:
Process p = new Process();
p.StartInfo = new ProcessStartInfo(#"C:\temp\test.exe");
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = true;
p.OutputDataReceived += new DataReceivedEventHandler(p_OutputDataReceived);
Task t = new Task(() =>
{
p.Start();
p.BeginOutputReadLine();
p.WaitForExit();
p.Close();
});
t.Start();
The p_OutputDataReceived handler just sends the received output to a textbox. This works and I have tested it on other programs, and there I get the output from the program when I expect.
However when I run my parser (the one created with py2exe) I get all outputs just after the parser has finished. So in the end I get all the correct output, but I get them all at the same time...
(note, I don't get one big output, but rather all the expected outputs but still, all at the same time)
So to be perfectly clear here:
If I run the parser from the command window, I get the outputs one by one
I have tested to run a C# console program instead of the py2exe generated program and that works (I get the outputs one by one)
Python checks if sys.stdout (the program's standard output) is a console. If it is, Python flushes write buffers immediately so the user can see it. Otherwise writes get cached and are outputted all at once:
when the write buffer is full or
at program exit.
The logic behind this is better performance, since when redirecting stdout to a file or to other programs, typically no one cares when the output occurs.
You can fix this by including sys.stdout.flush() in strategic locations in your Python parser script (i.e. directly after printing status line).
Btw: You should be able to observe the same time-delay behavior if you redirect the output of your parser to more, for example:
C:\temp\test.exe | more

How to send series of commands to a command window process?

We have a few commands(batch files/executables) on our network path which we have to call to initialize our 'development environment' for that command window. It sets some environmental variables, adds stuff to the Path etc. (Then only whatever working commands we type will be recognized & I don't know what goes inside those initializing commands)
Now my problem is, I want to call a series of those 'working commands' using a C# program, and certainly, they will work only if the initial setup is done. How can I do that? Currently, I'm creating a batch file by scratch from the program like this for example:
file.Writeline("InitializationStep1.bat")
file.Writeline("InitializeStep2.exe")
file.Writeline("InitializeStep3.exe")
Then the actual commands
file.Writeline("Dowork -arguments -flags -blah -blah")
file.Writeline("DoMoreWork -arguments -flags -blah -blah")
Then finally close the file writer, and run this batch file.
Now if I directly execute this using Process.<strike>Run</strike>Start("cmd.exe","Dowork -arguments"); it won't run.
How can I achieve this in a cleaner way, so that I have to run the initialization commands only once? (I could run cmd.exe each time with all three initializers, but they take a lot of time so I want to do it only once)
As #Hakeem has pointed out, System.Diagnostic.Process does not have a static Run method. I think you are referring to the method Start.
Once you have completed building the batch file, then simply execute it using the following code,
Process p = new Process();
p.StartInfo.FileName = batchFilePath;
p.StartInfo.Arguments = #"-a arg1 -b arg2";
p.Start();
Note that the # symbol is required to be prefixed to the argument string so that escape sequence characters like \ are treated as literals.
Alternative code
Process.Start(batchFilePath, #"-a arg1 -b arg2");
or
ProcessStartInfo processStartInfo = new ProcessStartInfo();
processStartInfo.FileName = batchFilePath;
processStartInfo.Arguments = #"-a arg1 -b arg2";
Process.Start(processStartInfo);
More information
Process.Start method
Example of multi command batch file
dir /O
pause
dir
pause
Save this file as .bat and then execute using the Start method. In this case you can specify the argument with the command in the batch file itself (in the above example, the /O option is specified for the dir command.
I suppose you already have done the batch file creation part, now just append the arguments to the commands in the batch file.
Redirecting Input to a process
Since you want to send multiple commands to the same cmd process, you can redirect the standard input of the process to the take the input from your program rather than the keyboard.
Code is inspired from a similar question at: Execute multiple command lines with the same process using C#
private string ProcessRunner()
{
ProcessStartInfo processStartInfo = new ProcessStartInfo("cmd.exe");
processStartInfo.RedirectStandardInput = true;
processStartInfo.RedirectStandardOutput = true;
processStartInfo.UseShellExecute = false;
Process process = Process.Start(processStartInfo);
if (process != null)
{
process.StandardInput.WriteLine("dir");
process.StandardInput.WriteLine("mkdir testDir");
process.StandardInput.WriteLine("echo hello");
//process.StandardInput.WriteLine("yourCommand.exe arg1 arg2");
process.StandardInput.Close(); // line added to stop process from hanging on ReadToEnd()
string outputString = process.StandardOutput.ReadToEnd();
return outputString;
}
return string.Empty;
}
The method returns the output of the command execution. In a similar fashion, you could also redirect and read the StandardOuput stream of the process.
The Process.Run method that you mentioned, is that from the Process class in System.Diagnostics namespace? AFAIK, the Process type doesn't have either a static or instance method named Run. If you haven't already I'd try with the Start method on Process, either instance or static

Turning C output into C# input

I am currently trying to develop a program which takes the output of an existing program (written in C) and uses it as input (in C#). The problem I am having is that the existing program prints data in redundant format but it dynamically changes. An example could be a random name generator and I need to make a program that logs all of the random names as they appear.
Could I just pipe this and the output will be grabbed as it comes? The C program is run from a CLI.
You could redirect the output streams from the Process object to get direct access to it. You make a call and it will invoke an event of your choice when output is received. One of my questions may provide some explanation on how to do this - C# Shell - IO redirection:
processObject.StartInfo.RedirectStandardOutput = true;
processObject.OutputDataReceived += new DataReceivedEventHandler(processObject_OutputDataReceived);
/* ... */
processObject.Start();
processObject.BeginOutputReadLine();
And then later:
public void processObject_OutputDataReceived(object sender, DataReceivedEventArgs e) {
ProcessNewData(e.Data);
}
Note that this includes the trailing newline.
Or you can just pipe it and use Console.ReadLine to read it in. On the command line, you would execute:
cprogram | csharp_program
Unfortunately, you can't do this directly with the Process object - just use the method above. If you do choose to go this route, you can use:
string input = "";
int currentChar = 0;
while ( (currentChar = Console.Read()) > -1 ) {
input += Convert.ToChar(currentChar);
}
Which will read from the input until EOF.
If the C# program is also command-line based, running this command from the command-line will chain them together:
my_c_program.exe | my_csharp_program.exe
The C# program will receive the output of the C program, via its standard input stream (Console.Read, etc.)

Categories