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`
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?
My console application has a void in it which opens an external console application, to compare 2 text files.
I get no errors, so I assume it's working. But when I look at the output, I see NOTHING.
When I open the application that compares the text files, it's working perfectly. So I think there must be something wrong with the void.
Here's my code. I used a combination of examples from MSDN as well as stackoverflow and other websites. But nothing so far. Maybe it's really obvious and I'm just stupid haha
using System.IO;
using System.Security.Permissions;
using System.Diagnostics;
static void Compare()
{
Process Compare = new Process();
try
{
Compare.StartInfo.UseShellExecute = false;
Compare.StartInfo.FileName = #"C:\Path\To\The\File.exe";
Compare.StartInfo.CreateNoWindow = true;
Compare.Start();
Compare.Kill();
}
catch (Exception)
{
Compare.Kill();
}
}
If anyone can tell me what is wrong with it, I would appreciate it! :)
You are killing it right after starting it
Compare.Start();
Compare.Kill();
Remove the Compare.Kill(); line and try again.
In addition, if you want to receive detailed output from the started process you will have to use async events:
Process process = new Process();
process.OutputDataReceived += new DataReceivedEventHandler(process_OutputDataReceived);
process.Exited += new EventHandler(process_Exited);
process.ErrorDataReceived += new DataReceivedEventHandler(process_ErrorDataReceived);
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
First off you seem to kill it staright away after starting it, so unless it can do what its got to do in like nanoseconds it will never output anything
You're killing the Process right after you start it.
If the Process exits on its own, you can do the following:
Compare.StartInfo.UseShellExecute = false;
Compare.StartInfo.FileName = #"C:\Path\To\The\File.exe";
Compare.StartInfo.CreateNoWindow = true;
Compare.Start();
Compare.WaitForExit();
If you would only like to give it so much time to execute:
Compare.WaitForExit(5000); //Wait 5 seconds.
This problem is bizarre. I have never encountered anything like it before.
I am trying to make my program extract a file using 7zip. I have done this before in other programs and it was never too difficult. So I copy and pasted my code in:
Process process = new Process
{
StartInfo =
{
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden,
FileName = "7za.exe",
UseShellExecute = false,
ErrorDialog = false,
Arguments = "x -y -o\"" + outputPath +"\" \""+ inputFile +"\"",
RedirectStandardOutput = false,
}
};
process.Start();
Immediately after this code has run my application terminates. It just disappears. It's certainly not meant to! I used the step into function and ran it. As soon as process.Start(); had finished the program closed and returned me into Visual C#. It didn't run any Application.Exit(); or anything, it just went away. There was no error awaiting me in Visual C#.
So I tried adding a MessageBox.Show("Test"); to the end. Ran it in step mode. Did process.Start(); fine, as soon as it executed the MessageBox code it disappeared again. I didn't even click ok in the message box (which showed up for about 0.2 seconds before the application terminated)
Well if this code is in Main() then it will finish when your code is done executing. Can you post the full code where this snippet is contained?
You can wait for the process to finish by including
process.WaitForExit();
Checking the ExitCode might tell you if your process succeeded. And you can always redirect the StandardError to check the output of that too.
process.StartInfo.RedirectStandardError = true;
string error = process.StandardError.ReadToEnd();
Try process.WaitForExit() after process.Start() ...
This question already has answers here:
C equivalent of autoflush (flush stdout after each write)?
(3 answers)
Closed 3 years ago.
I'm developing a control for a website, where user can upload his PDF, and after the upload a 3rd party CLI-tool launches to verify PDF against certain profile and generate a corresponding report.
The tool mentioned is callas pdfToolbox 4 (available here http://www.callassoftware.com/callas/doku.php/en:download[^])
The problem is that on my control at the website I need to display in real-time progress bar of checking the PDF-file. All the AJAX-stuff for this functionality is already written (ajax-postbacks, updates of progress-bar, etc), but there's a problem with asynchronous updates from the process, which launches the pdf-checking tool.
If you launch the tool from command-line window, you can see that, it generates output into standard output stream, which contains progress updates (in percents), as well as possible messages about errors in the PDF-file.
However, if the tool is launched by the process which I create in my web-control, I don't receive the OutputDataReceived events until the check has been finished, and then many OutputDataReceived events come at once, one after one.
My code is the following (I've written a small console-app to test things faster):
class Program
{
static string appString = "path-to-callas-cli";
static string argString = "path-to-pdf-and-path-to-report-and-path-to-callas-profile";
static void Main(string[] args)
{
ProcessStartInfo pInfo = new ProcessStartInfo(appString, argString);
pInfo.UseShellExecute = false;
pInfo.CreateNoWindow = true;
pInfo.RedirectStandardOutput = true;
pInfo.RedirectStandardError = true;
pInfo.RedirectStandardInput = true;
pInfo.ErrorDialog = true;
Process process = new Process();
process.StartInfo = pInfo;
process.OutputDataReceived += new DataReceivedEventHandler(process_OutputDataReceived);
process.Exited += new EventHandler(process_Exited);
process.ErrorDataReceived += new DataReceivedEventHandler(process_ErrorDataReceived);
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
Console.ReadKey();
}
static void process_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
}
static void process_Exited(object sender, EventArgs e)
{
}
static void process_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
Console.WriteLine("Received async output: " + e.Data);
}
}
As I've said, all the output from Callas CLI comes at once in the end (while the check takes about 35 seconds). To test my own code, I've created a small console app, which outputs numbers 10-20-30-....-100 in 500 milli-second intervals, and it's output is displayed perfectly from the main app, coming with 500ms intervals.
Any ideas?
I've had the exact same problem, Calling a CLI app from C# .NET from which I needed to get realtime stdOut updates (progress report of an archiver), but it didn't send any output until after the process exited (Not helpful to get a progress report after 10 minutes of compressing)
From what I understand it is the process's fault, not flushing the stdOut buffer. I couldn't find any way on .Net to manually tell the process to flush its stdOut. However, I found a hacky solution to be able to read the stdOut in realtime.
All I did was access the Process.StandardOutput.BaseStream.ReadByte() method which does work and returns actual bytes sent to the stdOut. This is pure data including carriage manipulation etc.
Then convert the byte (seems to be ASCII?) to a character with Char.ConvertFromUtf32(..) and push the character into a stringBuilder object. And now, since I have stdOut data, I can deal with it however I want.
Example: For my case, I wanted to capture the stdOut word by word and then callback to user to handle each word.
// - Standard process
proc = new Process();
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.CreateNoWindow = true;
proc.StartInfo.filename = "...";
proc.start();
// - Init some things
int byte_r; // The byte that is going to be read from stdOut
StringBuilder word = new StringBuilder(); // Append the characters here
Action<String> onStdOutWord; // USERSET from before. Callbacks words read from stdOut
// As long as there is stdOut Data
while( (byte_r = proc.StandardOutput.BaseStream.ReadByte()) > -1 )
{
// If SPACE or ENTER callback the current word
if(byte_r==32 || byte_r==13) {
if(word.Length>0) {
onStdOutWord(word.ToString());
word.Clear();
}
}else{
// Append character to string, skip special characters
if(byte_r>32) {
word.Append(Char.ConvertFromUtf32(byte_r));
}
}//-
}// - end while
Then, using the custom callback onStdOutWord() I was getting realtime data from the CLI app and handled it to get what I wanted.
The above is code just a use case since I wanted to get words, you can alter it and have it work like however you want. e.g. Not splitting into words and pushing out the entire stringBuilder object?
I know it's 8 years after the original question here, but I spend too much time researching on why I couldn't get stdout data like the OP, and figured to share my solution in case anyone else has the same problem and stumbles upon this page, like I did 😌
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);
}