Instead of using the object StreamReader
returned by property Process.StandardOutput
(documentation), to capture synchronously the output of the process you fired, you could use the event Process.OutputDataReceived
(documentation) to capture the output of the triggered process asynchronously.
To do this you need to pass one Event Handler, with the signature of DataReceivedEventHandler Delegate
(documentation) for the event Process.OutputDataReceived
.
How are you using MessageBox.Show()
, i imagine you are using Windows Forms, and in that case I suggest you display the output of the process shot in a TextBox
, instead of flaunting it with MessageBox
. For this it is important to pass the reference of your Form
to the property Process.SynchronizingObject
(documentation), otherwise your Event Handler will run in a thread other than your GUI thread, and would error if you tried to access your TextBox
from within the Event Handler.
The code would look like this:
private void OpenCliente()
{
Process processo = new Process();
processo.StartInfo.WorkingDirectory = Application.StartupPath + @"\Debug";
processo.StartInfo.FileName = @"app.exe";
processo.StartInfo.Arguments = "args";
processo.StartInfo.Verb = "runas";
processo.StartInfo.LoadUserProfile = true;
processo.StartInfo.UseShellExecute = false;
processo.StartInfo.CreateNoWindow = true;
processo.StartInfo.RedirectStandardOutput = true;
processo.StartInfo.ErrorDialog = true;
//processo.EnableRaisingEvents = true;
processo.SynchronizingObject = this;
processo.OutputDataReceived += (sender, e) => {
if (!String.IsNullOrEmpty(e.Data))
{
txtConsole.Text += e.Data + Environment.NewLine;
}
};
processo.Start();
// Dá início ao processo de leitura do output, e a partir
// daqui o event handler será disparado a cada linha do output.
processo.BeginOutputReadLine();
processo.WaitForExit();
}
But I also researched this locking problem that you reported, and found an answer in the OSEN where the author said this probably occurs because the output is too large and exceeds the capacity of the internal buffer:
Processstartinfo Hanging on "Waitforexit"? Why? - Stack Overflow
EDITION 1
I researched the subject a little further, and looking at the class source code Process
(Reference Source, Startwithcreateprocess function) I could see that the object StreamReader
created for property Process.StandardOutput
has a buffer internal with equal size 4096, that is, 4 KB:
if (startInfo.RedirectStandardOutput) {
Encoding enc = (startInfo.StandardOutputEncoding != null) ? startInfo.StandardOutputEncoding : Console.OutputEncoding;
standardOutput = new StreamReader(new FileStream(standardOutputReadPipeHandle, FileAccess.Read, 4096, false), enc, true, 4096);
}
So, when the result that would be displayed on screen by the application fired by your application exceeds 4 KB, the internal output buffer is full, and apparently that is what is causing your application to lock.
ISSUE 2
As a test, you could open a Command Prompt (cmd.exe) and run by the command line this application you fire from your program, with the same arguments, redirecting the output to a text file:
C:\PastaSuaApp\Debug>app.exe args > app_saida.txt
So you could check the size of the text file created, and you could check if this 4 KB theory is true. Also do a test with arguments that you know do not lock your program, to see if the output was below 4 KB.
ISSUE 3
I forgot to comment that if it is not possible to put one TextBox
to display the output of the target application in real-time, it is also possible to play the output result for a StringBuilder
, as in the event documentation example Process.OutputDataReceived
, and show that result at the end, the way you were doing:
using System.Text;
//[...]
private StringBuilder output = new StringBuilder();
private void OpenCliente()
{
Process processo = new Process();
processo.StartInfo.WorkingDirectory = Application.StartupPath + @"\Debug";
processo.StartInfo.FileName = @"app.exe";
processo.StartInfo.Arguments = "args";
processo.StartInfo.Verb = "runas";
processo.StartInfo.LoadUserProfile = true;
processo.StartInfo.UseShellExecute = false;
processo.StartInfo.CreateNoWindow = true;
processo.StartInfo.RedirectStandardOutput = true;
processo.StartInfo.ErrorDialog = true;
processo.OutputDataReceived += (sender, e) => {
if (!String.IsNullOrEmpty(e.Data))
{
output.Append(e.Data + Environment.NewLine);
}
};
processo.Start();
// Dá início ao processo de leitura do output, e a partir
// daqui o event handler será disparado a cada linha do output.
processo.BeginOutputReadLine();
processo.WaitForExit();
MessageBox.Show(output.ToString());
}
ISSUE 4
The author reported that when running the third-party program using the solution I suggested, from asynchronous output redirection, nothing was displayed, but that when the program ran directly on the console, the output was displayed normally, but also that when doing a redirect on the console using app.exe args > app_saida.txt
, nothing was saved in the text file.
I found that question on Soen where the author had a problem much like this in question, and a user suggested which could simply be a bug in the program and asked if the author contacted the program provider. The author later confirmed that it was a bug in the program, admitted by the supplier, after contact. So if there’s any possibility of contacting the supplier, maybe this is a way out.
But, another way I thought to solve the problem would be to simply start the process using the shell of the operating system itself, through the ProcessStartInfo.UseShellExecute
and let you create a window for the process, with the property ProcessStartInfo.CreateNoWindow
.
An example of code:
var p = new Process();
p.StartInfo.FileName = "ping.exe";
p.StartInfo.Arguments = "pt.stackoverflow.com";
p.StartInfo.UseShellExecute = true;
p.StartInfo.CreateNoWindow = false;
p.Start();
p.WaitForExit();
If you want the screen to be displayed for a while after the end of the process, you can add the time in milliseconds as an argument in the method WaitForExit()
.
But if you need the console screen to remain open until the user closes it, you can execute the command cmd.exe /k <linha-de-comando>
, the only problem is that at the end of the process the console will be available for the user to interact:
var p = new Process();
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.Arguments = "/k ping.exe pt.stackoverflow.com";
p.StartInfo.UseShellExecute = true;
p.StartInfo.CreateNoWindow = false;
p.Start();
p.WaitForExit();
Caio, do you have any error message? put here.
– Adjair Costa
@Adjaircosta no error message. The application simply stops after running Standardoutput.Readtoend().
– Caio de Paula Silva
have tried to make a simple console application and use this code ? apparently, should work ok. already this app.exe application has no way of knowing... is yours ? there is some documentation ?
– Rovann Linhalis
I don’t believe that applying this concept to Consoles will make any difference @Rovannlinhalis, however, I will take the test. I appreciate the advice. I hid the name of the application so that the question could be as generic as possible. It was purposeful. Any external application with the characteristic of being in looping should serve.
– Caio de Paula Silva
yes, my intention is to know whether this "app.exe" you did or not...
– Rovann Linhalis
No. This app.exe is not my own. It is a socket system, which regardless of whether it can connect or not, loops as soon as it is called. This considering that the command for connection is passed at that very instant. What I would like is to be able to read the return that the application produces. When the run via prompt I am able to read it.
– Caio de Paula Silva