I'm trying to put together a wrapper around a console application using StandardInput and StandardOutput. I'm getting stuck where the console application would prompt for input such as a password.
I'd like to read from StandardOutput, prompt the user using the read text, and write the user's input back to the console application using its StandardInput. Seems easy enough, here's what I have currently:
Process process = new Process()
{
StartInfo =
{
FileName = "bin\\vpnc.exe",
Arguments = "--no-detach --debug 0",
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardInput = true,
RedirectStandardOutput = true,
}
};
process.OutputDataReceived += (s, args) =>
{
textBlock1.Dispatcher.Invoke(new Action(() =>
{
textBlock1.Text += args.Data;
}));
};
process.Start();
process.BeginOutputReadLine();
The problem is that BeginOutputReadLine() is doing just that...waiting for a line ending. In this case it just sits, and sits, and sits because there is no line to read...the console application has written out text with no line ending and is waiting for input. Coincidentally, when I manually kill the process the event fires and I get the text.
Is there a way to tell that the process is waiting for StandardInput? Or am I missing a completely obvious way to accomplish the goal?
Unless you need something asynchronous you probably want ReadToEnd.
Here is a list of all StreamReader Methods
process.StandardOutput.BaseStream.BeginRead(...) is a potential substitute for your readline, and that will not wait for a line ending however you'd need to know what terminates the output to figure out when not to start wait for the next chunk of data
As Rune said, you can access directly to the output stream of the process (process.StandardOutput) and read from there (if you don't want to wait until a line break is entered into the console app), but this means that you need to check periodically for new data.
To interact with the application, you can just write to the StandardInput of the process (create a StreamWriter that writes to the process.StandardInput).
A nice sample of writing to it is on the MSDN documentation (http://msdn.microsoft.com/en-us/library/system.diagnostics.process.beginoutputreadline.aspx).
Hope this helps
You need to use the synchronous read method and handle any necessary threading yourself. The below code won't tell you that input is expected, but you will be able to detect that a prompt is displayed.
char[] b = new char[1024];
while (!process.HasExited) {
int c = process.StandardOutput.Read(b, 0, b.Length);
context.Response.Write(b, 0, c);
Thread.Sleep(100);
}
Related
Hello so I'm currently developing a program to help me automate the functionality of a python script, after a lot of research I found a way to launch and read the output of the python script to a C# console window but I want to implement a time-based condition for it to stop (close process after 5 minutes for example). My code for opening the process is as followed (credits to the original writer):
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "C:\\Python27\\python.exe",
Arguments = cmd,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true
},
EnableRaisingEvents = true
};
process.ErrorDataReceived += Process_OutputDataReceived;
process.OutputDataReceived += Process_OutputDataReceived;
process.Start();
process.BeginErrorReadLine();
process.BeginOutputReadLine();
process.WaitForExit();
Console.Read();
As you can see the process waits for it to be finished but the script can sometimes stall or not function correctly, I would like to add a time based condition to close the process if it's not completed after 5 minutes. I record the time by using the StopWatch class but having trouble targeting the process to close it, 5 different processes of the python script run at once so closing the python process would not be viable. I also use process.WaitForExit(); which waits until the process has finished but like I said above this isn't always the case so it's causing problems with processes never closing.
TL;DR: If process takes longer than 5 minutes to complete -> Force close
Any suggestions please?
Trying to use Process class and it's async features. Couldn't figure out how to read all output from Process before the program exits. Please help!!
Here's my code,
void RunProcess()
{
Process process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = CmdName,
Arguments = CmdArgs,
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false
}
};
using(process)
{
process.OutputDataReceived += Process_OutputDataReceived;
process.Start();
process.BeginOutputReadLine();
process.WaitForExit(); // Even waiting for exit here.
_logger.Debug("End of process");
}
} // void RunProcess()
void Process_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
if (!String.IsNullOrEmpty(e.Data))
_logger.Debug($"\t{e.Data}");
}
I have a other code that runs for at least another 30 - 45 secs after RunProcess() method is called and done, but don't see the output from my process anywhere in the logs.
If I run the program synchronously, I get all the output. But see no output when run async. Does anyone know what I am doing wrong, please?
(Updating question to make it more clear!)
The code I posted above works, and is minimal ( stripped out validations, classes, etc). I am looking for suggestions on how to make my program stop until full output is captured in log files. Does anyone know if there is a way to make it happen with the combination of WaitForExit and event call like I have in code above, please? It seems the process is completing first and terminating the event handler before it could print the log lines.
Many Thanks in advance!!
You can organize your code a little better. Make some class to contain your process. Take a look at how to do async/await.
Remove the line below and read the output from the process its self instead of delegating it.
process.OutputDataReceived += Process_OutputDataReceived;
How and When to use `async` and `await`
I am writing an application to manage processes and handle failovers. This program is written in C# for .NET Core and will run on Ubuntu Server 16.04 x64.
I have this code to create processes and track them, with exit events and such
ProcessStartInfo psi = new ProcessStartInfo
{
WorkingDirectory = "/home/xyzserver/someprocess",
FileName = "mono",
Arguments = "someprocess.exe",
RedirectStandardOutput = true
};
_proc = Process.Start(psi);
_proc.EnableRaisingEvents = true;
_proc.Exited += ProcOnExited;
I understand from the docs here that calls to Console.WriteLine will block if the _proc.StandardOutput stream is full. I want to prevent this behavior and dispose all the output from the managed application, since it will also write to a physical log on its own.
In addition, I would like to avoid storing any of the output in any unused stream buffers since they will never be used. A preferred solution will not UseShellExecute.
I have considered adding these 2 lines in the hope that any received data will be disposed, but am unsure about correctness.
_proc.OutputDataReceived += (sender, eventArgs) => {};
_proc.BeginOutputReadLine();
Is there a better way to accomplish this? Thoughts or comments are appreciated.
I manually ran the test on .NET Core using 3 programs:
An HTTP server to track the TextOutputter program.
A TextOutputter program that prints 1000 characters and makes an HTTP request every second.
a ProgramRunner that runs one instance of TextOutputter.
Without the 2 lines, the buffer fills upto 64k and stalls. With the 2 lines, there is no stalling.
I have a third party DOS process which writes data about its progress to the command line.
I want to react on the progress. Normally I would use a Process with RedirectStandardOutput = true and RedirectStandardError = true and then
.OutputDataReceived +=xyzOutputDataReceived;
.ErrorDataReceived += xyzErrorDataReceived;
.Start();
.BeginOutputReadLine();
.BeginErrorReadLine();
Normally this works. and I got what i need as DataReceivedEventArg.
In this case the process seems to update the same line it has written (how is that possible?), so it writes 15 %, 15% changes to 18% and so on. Only at the end of execution it seems that the data is flushed to StandardOutput.
Also if i just try to pipe data to a text file (eg odb.exe >> output.txt) it shows nothing.
Is there any way to get the temporary data?
The question is not about getting the Standard Output, this works fine (synchronously and asynchronously). It is about how to get output from a process which I cannot change, and which does not seem to flush it's output to the standard stream.
Like juharr says, you need to use Win32 to screen scrape the console.
Fortunately you don't need to write that code yourself. You can use the buffer-reader from this post: https://stackoverflow.com/a/12366307/5581231
The BufferReader reads from standardout. I suppose you are writing a wpf or winforms application so we'll also have to get a reference to the console window of the DOS application. For this, we will use the Win32 API call AttachConsole.
[System.Runtime.InteropServices.DllImport("kernel32.dll")]
private static extern bool AttachConsole(int pid);
I wrote a small example program that demonstrates the usage. It starts the exe and attaches to its console. It then scrapes the entire window once a second, and dumps the output to the debugger output window. You should be able to modify this to search the console content for any keywords etc. that you can use to track the progress of the program. Or you could dump it to a textfield or something in your UI, possibly after comparing it for changes?
var process = Process.Start(#"..path to your exe....");
//Wait for the DOS exe to start, and create its console window
while (process.MainWindowHandle == IntPtr.Zero)
{
Thread.Sleep(500);
}
//Attach to the console of our DOS exe
if (!AttachConsole(process.Id))
throw new Exception("Couldn't attach to console");
while (true)
{
var strings = ConsoleReader.ReadFromBuffer(0, 0,
(short)Console.BufferWidth,
short)Console.BufferHeight);
foreach (var str in strings.
Select(s => s?.Trim()).
Where(s => !String.IsNullOrEmpty(s)))
{
Debug.WriteLine(str);
}
Thread.Sleep(1000);
}
Good Luck!
I see several questions about how to launch processes and push data into stdin, but not how to control where their output goes.
First here is my current code, run from a console mode C# application:
// Prepare the process to run
ProcessStartInfo start = new ProcessStartInfo();
// Enter in the command line arguments, everything you would enter after the executable name itself
start.Arguments = " -";
// Enter the executable to run, including the complete path
start.FileName = "doxygen.exe";
// Do you want to show a console window?
start.WindowStyle = ProcessWindowStyle.Normal;
start.CreateNoWindow = false;
start.RedirectStandardInput = true;
start.UseShellExecute = false;
// Run the external process & wait for it to finish
using (Process proc = Process.Start(start))
{
//doxygenProperties is just a dictionary
foreach (string key in doxygenProperties.Keys)
proc.StandardInput.WriteLine(key+" = "+doxygenProperties[key]);
proc.StandardInput.Close();
proc.WaitForExit();
// Retrieve the app's exit code
int exitCode = proc.ExitCode;
}
What happens when I run this is I do not see any new window (though I think I should) and all of doxygen.exe's stdout is printed to my app's console window.
What I would like to happen is one of two things:
Doxygen is launched in a visible window, and I can see its stdout in that window, not in my app's window.
Doxygen is launched in a hidden window, and it's stdout is written to a log file.
How can I achieve these?
In addition, why am I not getting a separate window for the spawned process, and why is the spawned process writing output to my window not its own?
One thing that you can do is use RedirectStandardOutput and instead of using WaitForExit you can use ReadToEnd
ProcessStartInfo start = new ProcessStartInfo();
start.RedirectStandardOutput = true;
//make other adjustments to start
Process p = new Process();
p.StartInfo = start;
p.Start();
string output = p.StandardOutput.ReadToEnd();
and then you can use string output at your leisure
If you want to get output in real-time the p.StandardOutput property has methods that allow you to get the output asynchronously. I don't know all the details to it offhand, I've only used it once before, but there's plenty of literature out there if you search for it.
Also be careful when redirecting both StandardOutput and StandardError at the same time, If they're long enough, it is possible for that to cause deadlocks.
You need to do two things:
1) Indicate that you want the standard output of the process to be directed to your app by setting the RedirectStandardOuput property to true in the process.
2) BEFORE the call to WaitForExit, start capturing the output:
string sOutput = p.StandardOutput.ReadToEnd();
If you do not start reading the output before calling wait for exit, you can encounter a deadlock.
However, it is important to know that standard output will only capture output information, not anything written to the standard error stream of the app.
In order to capture both streams of information, you can hook the process's OutputDataReceived and ErrorDataReceived events and write the event data directly into a log file or store it in a class property for use after the process has completed.