How do I read the output of a loop-based process? [C#]

Asked

Viewed 680 times

13

Everything happens correctly to start the process, but when trying to get some kind of return of its execution through any of the available redirectors, be it input, output or error, the application hangs. And that’s because, I suppose, he accesses an application that’s running in looping (it’s a socket system), so when trying to access it, he 'latch' within it.

I imagine that the solution should be something like starting this process through one method and monitoring its return through another, however, I don’t know how to do this.

How to get a valid result for MessageBox.Show(processo.StandardOutput.ReadToEnd()) without locking all my application?

Method used:

private void OpenCliente()
{                                    
    Process processo = new Process();
    bool output;

    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.RedirectStandardInput = true;
    processo.StartInfo.RedirectStandardOutput = true;
    processo.StartInfo.RedirectStandardError = true;            
    processo.StartInfo.ErrorDialog = true;
    processo.Start();

    MessageBox.Show(processo.StandardOutput.ReadToEnd()); // Neste ponto o código trava
    processo.WaitForExit();
}     
  • Caio, do you have any error message? put here.

  • @Adjaircosta no error message. The application simply stops after running Standardoutput.Readtoend().

  • 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 ?

  • 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.

  • yes, my intention is to know whether this "app.exe" you did or not...

  • 1

    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.

Show 1 more comment

3 answers

9


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();
  • I appreciate the very well prepared answer! It is really a knowledge of great utility this to which you have determined to share, however I regret to have not yet been enough.

  • Didn’t it work? Why do you say it wasn’t enough? Give more details to try to help you.

  • The application hangs the moment I try to access the application to get the output. Testing with the code I provided, I can validate the thesis that it gets stuck inside this external application by the fact that by only finishing it (the application, not the application) by the task manager, the messagebox is then seen and the application goes back to normal.

  • So, but this is probably explained by that link I sent you, apparently the internal output buffer of the application you shot was full. You tried the solution I passed?

  • 1

    See this other link: What are the default values for Streamreader?. It says that the default size of the object buffer StreamReader is 1024 bytes. Probably the output of the application you fired is higher than that, and so your application hangs. As I suggested, you do not fill the buffer, because it takes line by line the output.

  • Yes, you’re right! For some reason I don’t understand I needed to give the start more than once so that I could start the application. And despite some instability, in some tests I was able to see Messagebox (probably only a few times for the reason you mentioned so much, the internal buffer overflow). In addition the Messagebox also came empty, but I think this is another five hundred. You’ve been of great help! Thank you. I will take some more time to evaluate everything, but it seems to me your answer is correct to the question. If not, at least the bonuses will be.

  • @Caiodepaulasilva, did you manage to do the tests? I was curious to know if the theory is right, and if it went well.

  • Yes, @Pedro Gaspar. I did some tests but I’m still not satisfied with the result. I’ve been trying to develop something more from all the assistance you’ve provided because I believe that failure is a reflection of something very punctual... The attempt to direct the application output to a text file by running it from cmd did not work. The file remains constantly empty. Whether filling with valid arguments or not. As for the other two codes provided, both show the same result: they work intermittently.

  • Checking the output via Console seems to be not much difference when compared to Messagebox, by the way. Sometimes it is possible to get it without locking everything and when not, I finish the application by task manager and voilà, the exit is visible. As I commented, another point that intrigues me is the always empty exit. When I run the program normally via cmd I am able to monitor everything perfectly visually. Anyway. Thank you very much. I feel I’m much closer to the solution.

  • 1

    @Caiodepaulasilva, I did some more research and found this question in Soen, where the author had a problem very similar to yours and in the end it was a bug in the third party program. Do you have how to contact the provider of this program that you are using? I edited my answer to include this information and also a possible solution to get around this problem, see if now will finally!

  • 1

    @Caiodepaulasilva, did this latest information I added help? Did you get in touch with the supplier? Could see the result of the program on screen, while running through your application?

  • I couldn’t get in touch, unfortunately. And yes, I can see the result on screen, but it is not my wish to make it available. My intention is really to keep it in the background.

  • @Caiodepaulasilva, I don’t know if I understand, what do you mean "but it is not my wish to make it available" and with "My intention is really to keep it in the background"? In the first example of 'Edit 4' of the reply the command prompt is not available and is closed as soon as the program is finished.

  • Sorry for the delay. Explaining, what I plan to do is not make visible the answer I am getting from this application. I am using Messagebox for now, because I am in tests, however, my intention is to keep everything running in the background really. I do not intend to raise any window or any information to the user, this is a routine for internal control.

  • I found that question, but again I cannot reproduce a valid result with it. Although I believe that it is very useful.

  • 1

    It may also be that the program is sending the output pro stream error! In one of the links I posted in the reply (that here, in Issue 4), the author redirected the console in this way: app.exe args > app_saida.txt 2>&1, which redirects including the error stream as well (when hitting eye on this command at the time I did not know what it was about, hehe, so I went research). You can use this command at the prompt to clear the doubt. If you keep leaving nothing in the text file, this is not it.

  • But why couldn’t you reproduce? What happened? It’s more or less the code that I had passed in my answer too, the only difference is that in this answer he’s also using the event ErrorDataReceived.

  • But what do you intend to do with the output of this application? Would you scan the output (which would go to the console) for information that would be stored internally? And this is the only way out of this application?

  • 1

    you have been right since issue one. Thank you so much for the support and patience. Out of ignorance I did not notice that your answer was correct. The method to access the outputs is just that. I had this epiphany to rescue the error message with total efficiency. As for the other output, it has still returned constantly empty, but this is a minor problem (and perhaps really specific to my case) and the question has already been answered. You provided the method. Anyway, I will keep trying and edit the question as soon as possible. Thanks again!

  • 1

    You’re welcome Caio, I’m glad it worked out somehow! But, depending on what it is, it would be better if you opened a new question, instead of editing this question that is already established and even has answers, so that the question and answers are not too extensive, and not to mix different problems.

Show 15 more comments

4

Whereas it is looped, have you tried using Peek? Example:

processo.Start();

while (process.StandardOutput.Peek() > -1)
{
    MessageBox.Show(processo.StandardOutput.ReadLine());

}
  • Very well thought out, I had not yet experienced it. However, it did not work.

-1

Change that:

processo.StartInfo.WorkingDirectory = (Application.StartupPath + @"\Debug");

That’s why:

processo.StartInfo.WorkingDirectory = (Application.StartupPath);

'Cause you’re just duplicating the folder on the way.

  • I don’t think you understand the purpose of this piece of code. The excerpt you highlighted identifies in which directory the application is working and says that from this point it needs to search the folder Debug. It is working perfectly in this sense. Besides, this does not serve me as an answer, perhaps rather as a comment. I advise rethinking the answer.

  • Ok, and your application is working where? In which folder?

Browser other questions tagged

You are not signed in. Login or sign up in order to post.